Language Reference/Terms/Arguments

From wiki.visual-prolog.com

< Language Reference‎ | Terms

Revision as of 16:18, 21 February 2019 by Thomas Linder Puls (talk | contribs) (Optional parameters: mandatoryOut)

This section describes named, default, optional parameters and functor originals. Even though these notions are individual they also have a significant impact on each other and therefore they are described together.

In brief:

  • Named parameters: In a call the actual arguments can be supplied by formal parameter name rather than position.
  • Default parameters: A default parameter value can be stated together with a formal argument in a predicate declaration. This value is used when no actual parameter is supplied.
  • Optional parameters: An actual parameter is optional and can therefore be skipped.
  • Functor originals: Functor term which is used as/bound to the original of another functor term (in a functor term expression).

Named parameters

The formal parameter names from a declaration can be used to specify the actual arguments in a call by using the syntax :<Formal> = <Actual>.

Example Given the predicate addPerson:
class predicates
    addPerson : (string Name, integer Height, integer Weight).

You can call addPerson using positional arguments like this:

addPerson("Hans", 175, 75)

But you can also use named parameters like this

addPerson(:Name = "Hans", :Height = 175, :Weight = 75)

Where Name, Height and Weight are the names that is used in the declaration.

When using named parameters the order of the parameters are insignificant, so this call has the same meaning:

addPerson(:Weight = 75, :Name = "Hans", :Height = 175)

Poisitional and named parameters can be mixed such that the first arguments are given by position and the last by name. Here the Name is given by position and Weight and Height are given by name

addPerson("Hans", :Weight = 75, :Height = 175)

Notice that all positional arguments must be to the left of the first named argument.

Default parameters

A default parameter is defined by adding = <value> after a formal input parameter in a declaration (<value> must be a compile time constant expression).

Example The predicate addPerson has three parameters Name, Height and Weight::
class predicates
    addPerson : (string Name, integer Height = 175, integer Weight = 75).

Height has default value 175 and Weight has default value 75.

Notice that it is illegal to provide default parameters for [out] parameters.

Optional parameters

If a parameter is [out] or if it has a default value, then the actual argument is optional and can be skipped provided that it is the last argument in the call. Skipping an [out] parameter corresponds to supplying an anonymous variable for that parameter. Skipping a parameter that has a default value corresponds to supplying the default value for the parameter.

Example Given this predicate
class predicates
    ppp : (integer A = 17, integer B [out]).

We can skip B because it is the last parameter and [out], so here we supply 12 for A and ignore the B output:

ppp(12) % corresponding to ppp(12, _)

In the call above 12 is the last argument, but there is a default value for that parameter so that argument can also be skipped:

ppp() % corresponding to ppp(17, _)

Using named arguments we can exchange the order of the parameters:

ppp(:B = Out, :A = 17)

In that call the last parameter has a default value so it can be skipped:

ppp(:B = Out) % corresponding to ppp(B: = Out, :A = 17) --> ppp(17, Out)

An optional out parameter can cause conflict with another predicate:

Example Conflicting predicates
predicates
    ppp : ().
    ppp : (integer X [out]). % conflict

These predicate declarations conflicts because a call without arguments p() could be to either of them.

The attribute [mandatoryOut] can be used to avoid such conflicts and/or if it doesn't seem appropriate that a predicate a has optional output parameters.

Example No conflict because the second predicate does not have optional output parameters:
predicates
    ppp : ().
    ppp : (integer X [out]) [mandatoryOut]. % no conflict

These predicate declarations conflicts because a call without arguments p() could be to either of them.

Functor originals

A functor original is a syntactic construction that describes an original for a functor term expression. Syntactically it takes the following form:

<functor>(<arguments> | <functor original> )

The functor original can either be a a functor value or a free variable. In both cases the construction indicates that the functor may have more arguments than those mentioned explicitly in front of the bar.

If the functor original is a functor value then it will be used as original for constructing a new functor value.

Example Consider this code:
domains
    person = person(string Name, integer Height, integer Weight).
 
class predicates
    updateName : (person Person, string NewName) -> person Updated.
clauses
    updateName(Person, NewName) = person(NewName | Person).

Here we use Person as original in the functor term expression person(NewName | Person). This expression will create a new person value which has NewName as the Name/first component. All the remaining functor components (i.e. Height and Weight) are taken from the original, i.e. Person.

The use of functor originals can be combined with named parameters:

domains
    person = person(string Name, integer Height, integer Weight).
 
class predicates
    updateWeight : (person Person, Integer NewWeight) -> person Updated.
clauses
    updateWeight(Person, NewWeight) = person(:Weight = NewWeight | Person).

Here NewWeight is used as Weight component. And again the remaining functor components (i.e. Name and Height) are taken from the original, i.e. Person.

The functor original can be any kind of term as long as it evaluates to an appropriate functor term.

Example Here the functor original is the result of calling a function:
clauses
    mkPerson(Name) = person(Name | getCurrentPerson()).

If a functor domain has more than one alternative, then the functor original must be of same kind as the term that is constructed.

Example Consider a functor domain with two alternatives:
domains
    transport =
        aircraft(string Name, integer Seats, integer Range);
        car(string Name, integer Doors).

The following is legal:

A1 = aircraft("Cruiser 1", 25, 300),
A2 = aircraft("Cruiser 2" | A1) % legal A1 is an aircraft

Because A1 is an aircraft, and it is an aircraft we are constructing (as A2).

This is illegal:

C = car("Taxi 23", 4),
A = aircraft(:Seats = 25, :Range = 173 | C)  % illegal C is a car

Because C is a car but we are constructing an aircraft, it does not matter/help that a car has a Name component, which is what we need for the aircraft we are constructing.

In a functor match the functor original can be an anonymous variable. And in that it simply indicates that the term can have more components which we don't care about.

Example Given the transport domain above, we can write a predicate that extract the Name component like this:
class predicates
    getName : (transport Transport) -> string Name
clauses
    getName(aircraft(Name | _)) = Name.
    getName(car(Name | _)) = Name.

Here we have used an anonymous variable as functor original indicating that the functor term has/may have more arguments (which we are not interested in).

In the car case the functor original only represents one additional argument, but using this form the code will be robust to adding additional components to car later. For the same reason, it can make sense to provide a functor original that doesn't represent any additional arguments (i.e. that represents zero extra arguments).

We could also have used named parameters:

class predicates
    getName : (transport Transport) -> string Name
clauses
    getName(aircraft(:Name = Name | _)) = Name.
    getName(car(:Name = Name | _)) = Name.


Here is a predicate that determines whether a transport is a car:

class predicates
    isCar : (transport Transport) determ.
clauses 
    isCar(car( | _)).

This example also shows that it is not necessary to provide any regular arguments at all besides the functor original.

In a functor match the functor original can be a named variable. If the variable is bound then it is used as an original for constructing a functor value and then that functor value is used in the match.

Example Consider this code
C = car("Taxi 23", 4),
car("Taxi 24" | C) = getCurrentCar()
In the second line C is used as original creating the term car("Taxi 24", 4). This term is then matched against the result of calling getCurrentCar.

If the functor original is a free variable (in a functor match) then that variable will be bound to the entire functor term:

Example Consider this code where Free is a free variable
car("Taxi 24" | Free) = getCurrentCar(),

This code is equivalent to this code:

Free = getCurrentCar(),
car("Taxi 24" | _) = Free,

I.e. Free is bound to the result of calling getCurrentCar and then it is matched to this functor pattern car("Taxi 24" | _).

Notice that the code will fail (and not raise an exception) if getCurrentCar returns an aircraft.

Also consider this code (where Name and Car are both free variables):

car(Name | Car) = getCurrentCar(),

it equivalent to this code

Car = getCurrentCar(),
car(Name | _) = Car