views:

364

answers:

3

Is it possible to write functions with dynamically typed input parameters? I tried pattern matching, but apparently it does not work like this.

I wish to do something like this:

fun firstStr (0,n:string) = n
  | firstStr (b:string,n:string) = if b>n then n else b;

Thank you.

+6  A: 

StandardML is a strict, statically typed language. Therefore, you can't have a function which accepts an int in the first case and a string in the second. The error you get is

this clause:        string * string -> 'Z
previous clauses:      int * string -> 'Z
in declaration:
  firstStr =
    (fn (0,<pat> : string) => n
      | (<pat> : string,<pat> : string) => if <exp> > <exp> then n else b)

If you want to have one case which is a string and one case which is an int, you can make a new type, a "tagged union" (aka "discriminated union"), which is designed to be easy to use with pattern matching. It would look like this:

datatype Wrapper = Int    of int
                 | String of string
fun firstStr(Int 0,    n:string) = n
  | firstStr(String b, n:string) = if b>n then n else b

Of course, you might want to find some more appropriate name for this Wrapper type, something that makes sense in the context of your programme. Also please note that the type annotation on n is not really necessary; it would be more idiomatic to write

fun firstStr(Int 0,    n) = n
  | firstStr(String b, n) = if b>n then n else b

Additionally, the compiler will tell you you have left a case uncovered: What if the first argument is an integer not equal to zero?

Finally, it's not really clear what you mean by the comparison b>n, what aspect of the two strings did you want to compare? I see that when I compare two strings in SML, I see a lexicographic (aka alphabetic) comparison. Is that what you wanted?

harms
+1  A: 

Polymorphic variants in OCaml have more of the dynamic property you are looking for. You can take a look if you want, OCaml and SML are very close languages.

Pascal Cuoq
+4  A: 

To elaborate a little bit, suppose you have two arguments, each of which could be a string or an integer, and if you have two strings you want the lexicographically smaller string, if you have one string you want that string, and if you have two integers you can't return a string. What do you do? Return a value of type string option (look up option, SOME, and NONE at http://www.standardml.org/Basis/option.html):

datatype string_or_int = String of string
                       | Int    of int 

fun firstStr(String a, String b) = SOME (if a < b then a else b)
  | firstStr(String a, Int _   ) = SOME a
  | firstStr(Int _,    String b) = SOME b
  | firstStr(Int _,    Int _   ) = NONE

Function firstStr has type

string_or_int * string_or_int -> string option

The fastest way to becoming a proficient ML programmer is to learn to think about types first. If for example, what you really wanted was a function of type string option * string -> string, you wouldn't need to write it yourself; the builtin function getOpt does that. On the other hand, it sounds like you want string option * string -> string, so you can write

fun firstStr(SOME a, b) = if a < b then a else b
  | firstStr(NONE,   b) = b

and you dont' need a SOME value constructor or an option type on the result.

Norman Ramsey
string option type was exactly what I was looking for:fun firstStr (NONE,n:string) = SOME n | firstStr (SOME b,n:string) = SOME (if b>n then n else b);
Artium