Can you have a function with and optional parameter as the first and only parameter?
thus
(defn foo [& bar] (if (= bar) 1 2))
What & bar means at function definition is that all the rest of the arguments will be put into a list. It does not give any guarantees as to the size of the list, so it could be empty, with one or more items.
A better approach to having one and only optional argument, is to have it accept zero or one arguments:
(defn foo
([] (foo 12))
([bar] (if (= bar 12) 1 2)))
In this example, if you call the first function definition, with zero arity, it will simply call the second 1-arity function definition with a default value of 12.
This will work:
(defn foo [& bar]
(if (seq bar) 1 2))
Using seq is the way you are supposed to code 'not empty'. Not to be confused with seq?, which means something quite different, which will in fact return true here.
The important point is that bar becomes a list inside the function body. If you want to get this list back to what it was when you called the function then use apply, or deconstruct the list in the function itself, as being explained in the comments below...
Related
I just spent an embarrassing amount of time figuring out that if you're passing a parameterized datatype into a higher-order function in SML, it needs to be in brackets (); so, for example:
fun f1 p = f2 p will work when called like this (for example): f1(Datatype(parameter)) but will not work if called like f1 Datatype(parameter). I'm sure there's a very simple reason why, but I'm not quite clear. Is it something like, the datatype and parameter are "seen" as 2 things by the function if not in brackets? Thanks!
It's important to realize how functions work in SML. Functions take a single argument, and return a single value. This is very easy to understand but it's very often practically necessary for a function to take more than one value as input. There are two ways of achieving this:
Tuples
A function can take one value that contains multiple values in the form of a tuple. This is very common in SML. Consider for instance:
fun add (x, y) = x + y
Here (x, y) is a tuple inferred to be composed of two ints.
Currying
A function takes one argument and returns one value. But functions are values in SML, so a function can return a function.
fun add x = fn y => x + y
Or just:
fun add x y = x + y
This is common in OCaml, but less common in SML.
Function Application
Function application in SML takes the form of functionName argument. When a tuple is involved, it looks like: functionName (arg1, arg2). But the space can be elided: functionName(arg1, arg2).
Even when tuples are not involved, we can put parentheses around any value. So calling a function with a single argument can look like: functionName argument, functionName (argument), or functionName(argument).
Your Question
f1(Datatype(parameter))
This parses the way you expect.
f1 Datatype(parameter)
This parses as f1 Datatype parameter, which is a curried function f1 applied to the arguments Datatype and parameter.
Suppose I define a record called Node: (defrecord Node [tag attributes children]).
After this definition, according to the docstring of defrecord a factory function called ->Node is defined, as well as another factory function map->Node and a Java class constructor Node..
I'm wondering what exactly the difference is between the positional factory function ->Node and the constructor Node., apart from the normal differences between a Java class constructor / method on the one hand and a clojure function on the other (by normal differences I'm thinking things like the fact that functions are first-class in Clojure while methods are not).
(Update: see end of this answer for a note on primitive field types vs. parameter types of the ctor vs. parameter types of the factory.)
The positional factory just calls the constructor directly. The only interesting thing beyond that is that for records / types with large numbers of fields (namely over 20, which is the maximum number of positional arguments a Clojure function can accept) making the constructor call is slightly more involved (since you have to unpack some arguments from the rest-args seq); positional factories as emitted by defrecord and deftype handle that correctly, and moreover check that the correct number of arguments is supplied, throwing an appropriate exception if not.
This is documented in the docstring for the private function clojure.core/build-positional-factory; say (doc clojure.core/build-positional-factory) at the REPL to read it, or (source clojure.core/build-positional-factory) to see the source.
The end result looks roughly like this:
;; positional factory for a type with up to 20 fields
(defn ->Foo
"Construct a Foo."
[x y z]
(new Foo x y z))
;; positional factory for a type with many fields
(defn ->Bar
"Construct a Bar."
[a b c d e f g h i j k l m n o p q r s t & overage]
(if (= (count overage) 2)
(new Bar a b c d e f g h i j k l m n o p q r s t
(nth overage 0) (nth overage 1))
(throw
(clojure.lang.ArityException.
(+ 20 (count overage)) (name '->Bar))))))
A note on parameter types:
Not sure if this falls under the rubric of "normal differences", so I'll mention it explicitly: deftype / defrecord introduced classes may have fields of primitive types, in which case the corresponding parameters of the constructor will also be of primitive types. However, as of Clojure 1.5.1, the positional factories always take all-Object arguments, even if technically they could be declared as primitive-accepting functions (that is, if the primitive types involved are long and/or double and there are at most four positional parameters).
#{}, the empty set. Apart from the differences you've explicitly said you're not interested in, there are no other differences. ->Foo exists specifically because functions are more friendly than constructors.
Can anyone explain to me why
((fn ([x] x)) 1)
works and returns 1? (There's one "extra" set of parenthesis after the fn) Shouldn't it be the following?
((fn [x] x) 1)
Additionally,
((fn (([x] x))) 1)
(2 "extra" sets of parenthesis) fails with a "CompilerException System.ArgumentException: Parameter declaration([x] x) should be a vector". Why?
Thanks!
The extra set of parenthesis allows you to define a function taking a variable number of arguments. The following example defines a function that can take either one argument or two arguments:
(defn foo
([x] x)
([x y] (+ x y)))
You can see this as defining two functions under a single name. The appropriate function is going to be called depending on the number of argument you provide.
If you define a function with a fixed number of arguments, the two following forms are equivalent:
(defn bar ([x] x))
and
(defn baz [x] x)
With this in mind you can understand the compiler exception. You are trying to define a function as follows:
(defn qux
(([x] x)))
When using the extra set of parenthesis, closure expect the first element inside the parenthsesis to be a vector (within brackets). However in this case, the first element is ([x] x) which is a list and not a vector. This is the error you get.
I have a list of N elements in SML.
I want to apply a function to every element in that list, so I use map.
However the function I want to apply has more than 1 argument like this:
foo a b (c, d)
Where a is the element I am using from the list and b c and d are predefined variables that are the same every time.
I declare my function like this:
fun foo2 = map foo aList b (c,d)
but I get an operator and operand error, which was expected but I can think of any other way to do this.
fun foo2 list = map (fn x => foo x b (c, d)) list
It would be most convenient if the order of foo's args were changed; you can, of course, make a wrapper:
fun foo_swapped_args b (c,d) a = foo a b (c, d)
Then you could do
map (foo_swapped_args b (c,d)) aList
Context
Suppose I have protocols ICursor, IFoo, IBar then I can have a function named:
(defn IFoo->IBar [foo] ... )
Now, suppose I have a function which takes two arguments
x: ICursor
y: IFoo
and output an object of type IBar.
Now, is there any standard way to denote this in a function name? For example, none of the following work:
(defn ICursor,IFoo->IBar [x y] ...)
because "," is treated as space
(defn (ICursor, IFoo)->IBar [x y] ... )
because () is treated as function application.
(defn [ICursor, IFoo]->IBar [x y] ... )
because [] is treated as vector.
Question
Is there a standard way to encode protocol types of arguments in the function name?
Thanks!
I don't think there is any such recommended way and it seems like sort of type annotations. There is one such project around giving type annotations to clojure code at this link.
You can use something like : (defn ICursor->IFoo->IBar [x y] ...) which denotes that the function takes ICursor and IFoo as params and return IBar, so basically last type is return type and before that everything is parameter type but I am not sure if that can be a long term or idiomatic solution because then where is the method actual name :) which is important then type annotation.