zlotnleo
zlotnleo

Reputation: 285

SML use abstype with signatures

I'm writing a library to handle simple images in Standard ML. It should support different types used as colour for each pixel, e.g. bool, Word8.word, etc.

I've got a abstype 'a image with all common functions defined independent of representation ('a is the colour representation) , but the output formats differ, so I'd like to have different structures.

Is there a way to "open" an abstype inside a structure? I can only get this working in a very ugly way:

abstype 'clr absimage = Image of {width : int, height : int, data : 'clr array array}
with
    fun createFunc (w, h) f = Image {width = w, height = h, data = ...}
    fun createBlank (w, h) clr = createFunc (w, h) (fn _ => clr)
    ...
end
signature IMAGE = sig
    type colour
    type image
    val createFunc : (int * int) -> (int * int -> colour) -> image
    val createBlank : (int * int) -> colour -> image
    ...
    val toBinPPM : image -> string -> unit
end
functor ImageFn(C : sig type colour end) = struct
    open C
    type image = colour absimage
    val createFunc = createFunc
    val createBlank = createBlank   
    ...
end
structure Image8 :> IMAGE = struct
    structure T = ImageFn(struct type colour = Word8.word end)
    open T

    fun toBinPPM img filename = ...
end

In particular, the definition of the functor requires to write statements like val name = name for all functions defined in the with ... end part of abstype.

Or is my approach completely wrong?

This combination of abstype and signature is my attempt to recreate OOP's abstract class with common methods from abstype and require implementation of other methods in all structures using the signature

P.S. Why does SML disallow statements like open (ImageFn(struct ... end)) and forces to use a temporary structure (T in the above code)?

Upvotes: 4

Views: 612

Answers (1)

Andreas Rossberg
Andreas Rossberg

Reputation: 36088

There is no reason to use abstype in today's SML. Consider it deprecated. It is a relict of pre-module times. You can achieve the same effect of hiding the constructors of a type with structures, signatures and sealing (the :> operator), but in a more flexible and consistent manner. That also explains why it does not integrate nicely with modules -- it predates them and was essentially replaced by them.

In your concrete example, instead of using abstype, simply define image as a datatype directly in the body of the ImageFn functor, and hide its constructors with a signature annotation, like so:

signature IMAGE =
sig
  type colour
  type image
  val createFunc : int * int -> (int * int -> colour) -> image
  val createBlank : int * int -> colour -> image
  ...
end

signature IMAGE8 =
sig
  include IMAGE
  val toBinPPM : image -> string -> unit
end

functor ImageFn(type colour) :> IMAGE =
struct
  datatype image = Image of {width : int, height : int, data : colour array array}
  fun createFunc (w, h) f = Image {width = w, height = h, data = ...}
  fun createBlank (w, h) clr = createFunc (w, h) (fn _ => clr)
  ...
end

structure Image8 :> IMAGE8 =
struct
  structure T = ImageFn(type colour = Word8.word)
  open T
  fun toBinPPM img filename = ...
end

Edit: In fact, it isn't even necessary in this case to define image as a datatype. A plain type would do just as well and makes the code slightly simpler:

type image = {width : int, height : int, data : colour array array}

As for your PS question: yeah, I don't know either. There is no particular reason. Some SML dialects implement it as an extension.

Upvotes: 6

Related Questions