|This article contains preliminary documentation for Visual Prolog 9.
The relevant features may be absent or different in Visual Prolog 8.
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.
- 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 original: Functor term which is used as/bound to the original of another functor term (in a functor term expression).
The formal parameter names from a declaration can be used to specify the actual arguments in a call by using the syntax :<Formal> = <Actual>.
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.
A default parameter is defined by adding = <value> after a formal input parameter in a declaration (<value> must be a compile time constant expression).
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.
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.
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)
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.
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.
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.
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.
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.
C = car("Taxi 23", 4), car("Taxi 24" | C) = getCurrentCar()
If the functor original is a free variable (in a functor match) then that variable will be bound to the entire functor term:
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