functional-programmingwildcardsmlsmlnj

Trying to understand the SML option structure


Okay so I started learning SML for a class, and I'm stuck with the option structure. What I have so far for this example:

datatype suit = spades|hearts|clubs|diamonds;
datatype rank = ace|two|three|...|j|q|k|joker;
type card = suit*rank;

My lecturer was trying to explain the use of the option structure by saying that not all cards necessarily have a suit; jokers don't have a suit associated with them. So when designing a function getsuit to get the suit of a card, we have the following:

datatype 'a option = NONE | SOME of 'a;
fun getsuit ((joker,_):card):suit option = NONE
  | getsuit ((_,s):card):suit option = SOME s;

But using emacs, I get two errors, one saying how the pattern and constraint don't agree,

pattern: rank * ?.suit
constraint: rank * suit

and the other saying how the expression type and the resulting types don't agree.

expression: ?.suit option
result type: suit option

This was the code provided by the lecturer so clearly they're not of much help if it results in errors. What's the meaning of "?." and why does it show up? How would I correctly define this function?


Solution

  • Not really a problem with option as you've defined it. You've got the order of suit and rank in your card patterns the wrong way:

    Try:

    datatype 'a option = NONE | SOME of 'a;
    
    fun getsuit ((_, joker):card):suit option = NONE
      | getsuit ((s, _):card):suit option = SOME s;
    

    My version of ML probably prints errors differently so I'm not sure how to explain the the meaning of ?. etc. But it's simple enough if you take it bit by bit:

    Try

    (clubs, ace);
    

    The interpreter (or emacs if that's what you're using) tells you the type is a product of a suit * rank. That's ML's type inference at work, but you can specify the type (you expect) like this:

    (clubs, ace): suit*rank;
    

    Or

    (clubs, ace): card; (* Works, since card is defined as (suit*rank) *)
    

    And you won't have any complaints. But obviously you would if you did

    (clubs, ace): rank*suit;
    

    Or

    (clubs, ace): card; (* card is defined as (rank*) *)
    

    You placed a constraint on the type of getsuit's argument (that it must be a card, or the compatible (suit*rank) product), but the pattern is of type (rank*?) or (?*rank), neither of which are compatible with (suit*rank).