Edward Ned Harvey
Edward Ned Harvey

Reputation: 7031

Declaring type of function in SML

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

Answers (1)

sshine
sshine

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

Related Questions