Search code examples
smlml

Type clash in Standard ML datatype with same constructor name


I need to describe in Standard-ML a language made of properties and values. My property system is made of properties which can have values, like for example:

color: red | yellow | blue | transparent
align: left | center | right
bgcolor: red | yellow | blue | transparent

I created this sml file which tries to describe these properties:

datatype colorvalue = Transparent
                    | Yellow
                    | Blue
                    | Red

datatype bgcolorvalue = Transparent
                      | Yellow
                      | Blue
                      | Red

datatype alignvalue = Left
                    | Center
                    | Right

(* Generic property: it can be any of the above *)
datatype property = Color of colorvalue
                  | BgColor of bgcolorvalue
                  | Align of alignvalue

(* Some values *)
val prop1: property = Color   Transparent
val prop2: property = BgColor Transparent

When I compile this in MoscowML I get:

,File "c:\Users\myuser\documents\myproj\property.sml", line 21, characters 31-42:
! val prop1: property = Color   Transparent
!                               ^^^^^^^^^^^
! Type clash: expression of type
!   bgcolorvalue
! cannot have type
!   colorvalue

My guess

So I think that the problem is that color and bgcolor share a common property value: transparent which reflects in datatypes colorvalue and bgcolorvalue to share constructor Transparent. Actually they share all values, thus all constructors.

  • Is it the reason for this failure?
  • In any case, what should I do to describe my system?

Solution

  • It is easy to see that trying to use the same constructor in different types in the same scope would create problems with type inference. For example, what should the type of

    fun heat Transparent = Yellow
    |   heat Yellow = Red
    |   heat Red = Blue
    |   heat Blue = Blue;
    

    be? colorvalue ->colorvalue or bgbcolorvalue -> bgbcolorvalue or colorvalue -> bgbcolorvalue or bgbcolorvalue -> colorvalue?

    The easiest workaround would be to adopt different naming conventions for the constructors. You could also use structures (which is how SML keeps e.g. different uses of the name map in the basis library without any clashes). Something like:

    structure Color = struct
        datatype value = Transparent
                       | Yellow
                       | Blue
                       | Red
    end
    
    structure BGBColor = struct
        datatype value = Transparent
                       | Yellow
                       | Blue
                       | Red
    end;
    

    Then you can do things like:

    - val a = Color.Transparent;
    val a = Transparent : Color.value
    
    - val b = BGBColor.Transparent;
    val b = Transparent : BGBColor.value
    

    This last was run in the SML/NJ REPL and illustrates how there are now no clashes.