Reputation: 7031
I'm new to ML, but in other languages that use type inference, I have learned the habit of omitting the type of a thing whenever the inference on the right hand side is obvious to a human reader, and explicitly declaring the type of a thing whenever the inference is not obvious to a human. I like this convention, and would like to continue with it in my ML code.
I have the following example function declarations, that are equivalent:
fun hasFour [] = false
| hasFour (x::xs) = (x = 4) orelse hasFour xs
is equivalent to
val rec hasFour: int list -> bool =
fn [] => false
| (x::xs) => (x = 4) orelse hasFour xs
I like the latter form not only because it's easier for me to figure out what type the function is when I read it, but also because it explicitly declares the type of the function, and hence, if I screw something up in my implementation, there is no chance of accidentally declaring something that's syntactically valid but the wrong type, which is harder to debug later.
My question is: I want to use fun
instead of val rec
, because anonymous fn
's can only take one parameter. So the latter technique is not syntactically valid for a function like int -> int -> bool
. Is there a way to explicitly declare the type in fun
? Or have I named all the preferred alternatives in this post, and should simply follow one of these patterns? The only way I could find to use fun
with explicit type declaration was to add a type declaration to each parameter in the pattern, which is quite ugly and horrible, like so:
fun hasFour ([]:int list):bool = false
| hasFour (x::xs) = (x = 4) orelse hasFour xs
A colleague showed me some code following a pattern like this:
fun hasFour [] = false
| hasFour (x::xs) = (x = 4) orelse hasFour xs
val _ = op hasFour: int list -> bool
By declaring an unnamed variable and setting it to an instance of the function with a forced type, we effectively achieve the desired result, but the val _
must appear below the fully defined function, where it's less obvious to a human reader, unless you simply get used to this pattern and learn to expect it.
Upvotes: 4
Views: 1264
Reputation: 16145
I asked a very similar question, Can I annotate the complete type of a fun
declaration?, recently.
Your current solution would have been a nice answer to that.
You can have multiple curried arguments with multiple fn
, e.g. like:
val rec member : ''a -> ''a list -> bool =
fn x => fn [] => false
| y::ys => x = y orelse member x ys
Or you can do as you currently do, or as matt suggests:
local
fun member _ [] = false
| member x (y::ys) = x = y orelse member x ys
in
val member = member : ''a -> ''a list -> bool
end
But the combination of using fun
and having the complete type signature listed first is yet elusive.
For production-like code, the norm is to collect type signatures in a module signature. See ML for the Working Programmer, ch. 7: Signatures and abstraction, pp. 267-268. Although I imagine you'd want to use Ocaml then.
Upvotes: 2