0

I read about polymorphism in function and saw this example

 fun len nil = 0
   | len rest = 1 + len (tl rest)

All the other examples dealt with nil arg too.

I wanted to check the polymorphism concept on other types, like

fun func (a : int) : int = 1  
  | func (b : string) : int = 2  ;

and got the follow error

stdIn:1.6-2.33 Error: parameter or result constraints of clauses don't agree 
[tycon mismatch]
  this clause:      string -> int
  previous clauses:      int -> int
  in declaration:
    func = (fn a : int => 1: int
             | b : string => 2: int)

What is the mistake in the above function? Is it legal at all?

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
URL87
  • 10,667
  • 35
  • 107
  • 174
  • 2
    I would say it is sample of pattern matching in function definition, not polymorphism. – om-nom-nom Feb 26 '13 at 11:56
  • So what I trying to do with **polymorphism** is not possible among SML ? – URL87 Feb 26 '13 at 11:58
  • 2
    you see, the thing above works because empty list (aka `nil`) and non-empty list (represented here as 'rest') both belongs to the [same common datatype](https://www.cs.cornell.edu/Courses/cs312/2007sp/lectures/lec03.html): list. But `int` and `string` are not (but **you can define** your own datatype, say `datatype foobar = Int of int | String of string`). So actually there is no polymorphism in a sense you might think. – om-nom-nom Feb 26 '13 at 12:10
  • For more background, see also the answers to [the same question about Haskell](http://stackoverflow.com/questions/10811657/why-is-function-definition-for-all-types-at-once-not-allowed-in-haskell/10813898#10813898). – Andreas Rossberg Feb 26 '13 at 14:29

2 Answers2

2

Subtype Polymorphism:

In a programming languages like Java, C# o C++ you have a set of subtyping rules that govern polymorphism. For instance, in object-oriented programming languages if you have a type A that is a supertype of a type B; then wherever A appears you can pass a B, right?

For instance, if you have a type Mammal, and Dog and Cat were subtypes of Mammal, then wherever Mammal appears you could pass a Dog or a Cat.

You can achive the same concept in SML using datatypes and constructors. For instance:

datatype mammal = Dog of String | Cat of String

Then if you have a function that receives a mammal, like:

fun walk(m: mammal) = ...

Then you could pass a Dog or a Cat, because they are constructors for mammals. For instance:

walk(Dog("Fido"));
walk(Cat("Zoe"));

So this is the way SML achieves something similar to what we know as subtype polymorphism in object-oriented languajes.

Ad-hoc Polymorphysm:

Coercions

The actual point of confusion could be the fact that languages like Java, C# and C++ typically have automatic coercions of types. For instance, in Java an int can be automatically coerced to a long, and a float to a double. As such, I could have a function that accepts doubles and I could pass integers. Some call these automatic coercions ad-hoc polymorphism.

Such form of polymorphism does not exist in SML. In those cases you are forced to manually coerced or convert one type to another.

fun calc(r: real) = r

You cannot call it with an integer, to do so you must convert it first:

calc(Real.fromInt(10));

So, as you can see, there is no ad-hoc polymorphism of this kind in SML. You must do castings/conversions/coercions manually.

Function Overloading

Another form of ad-hoc polymorphism is what we call method overloading in languages like Java, C# and C++. Again, there is no such thing in SML. You may define two different functions with different names, but no the same function (same name) receiving different parameters or parameter types.

This concept of function or method overloading must not be confused with what you use in your examples, which is simply pattern matching for functions. That is syntantic sugar for something like this:

fun len xs =
  if null xs then 0
  else 1 + len(tl xs)

Parametric Polymorphism:

Finally, SML offers parametric polymorphism, very similar to what generics do in Java and C# and I understand that somewhat similar to templates in C++.

So, for instance, you could have a type like

datatype 'a list = Empty | Cons of 'a * 'a list

In a type like this 'a represents any type. Therefore this is a polymorphic type. As such, I could use the same type to define a list of integers, or a list of strings:

val listOfString = Cons("Obi-wan", Empty);

Or a list of integers

val numbers = Cons(1, Empty);

Or a list of mammals:

val pets = Cons(Cat("Milo", Cons(Dog("Bentley"), Empty)));

This is the same thing you could do with SML lists, which also have parametric polymorphism:

You could define lists of many "different types":

val listOfString = "Yoda"::"Anakin"::"Luke"::[]
val listOfIntegers 1::2::3::4::[]
val listOfMammals = Cat("Misingo")::Dog("Fido")::Cat("Dexter")::Dog("Tank")::[]

In the same sense, we could have parametric polymorphism in functions, like in the following example where we have an identity function:

fun id x = x

The type of x is 'a, which basically means you can substitute it for any type you want, like

id("hello");
id(35);
id(Dog("Diesel"));
id(Cat("Milo"));

So, as you can see, combining all these different forms of polymorphism you should be able to achieve the same things you do in other statically typed languages.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
1

No, it's not legal. In SML, every function has a type. The type of the len function you gave as an example is

fn : 'a list -> int

That is, it takes a list of any type and returns an integer. The function you're trying to make takes and integer or a string, and returns an integer, and that's not legal in the SML type system. The usual workaround is to make a wrapper type:

datatype wrapper = I of int | S of string

fun func (I a) = 1
  | func (S a) = 2

That function has type

fn : wrapper -> int

Where wrapper can contain either an integer or a string.

Tayacan
  • 1,896
  • 11
  • 15