3
signature MAPPABLE = sig
  type 'a mappable
  val fmap : ('a -> 'b) -> 'a mappable -> 'b mappable
end

structure Option : MAPPABLE = struct
  type 'a mappable = 'a option
  fun fmap f v =
    case v of
      (SOME x) => SOME (f x)
    | NONE => NONE;
end

structure List : MAPPABLE = struct
  type 'a mappable = 'a list
  fun fmap f v = map f v
end


fun incByFive x = x + 5

Really just to have a function that does stuff with fmap

fun mapToPair f x =
    let val b = List.fmap f x
    in (b,b)
    end

val lst = mapToPair incByFive [1,2,3];

Suppose you want to make a generic implementation, that works for all instances of MAPPABLE. The following does not work

fun mapToPair f x =
    let val b = MAPPABLE.fmap f x
    in (b,b)
    end

It seems, that SML people point to Functors, if that needs to be done. I tried implementing one, for a generic implementation of mapToPair

functor FMAPFUNCTOR (structure Q : MAPPABLE)
 = struct
   fun mapToPair f x =
       let val b = Q.fmap f x
       in (b,b)
       end
end;

However, to use it with what in Haskell I'd call a functor instance, I need to instantiate the functor (this reminds me of C++ templates for some reason)

structure MAPPABLE_TO_PAIR = FMAPFUNCTOR (structure Q = List);
val lstByPoly = MAPPABLE_TO_PAIR.mapToPair incByFive [1,2,3]

I would have to repeat that instantiation for every MAPPABLE instance I want to use. I guess Haskell performs something like this, too. Just implicitly.

Now I wonder if there is any shortcut / sugar for a better "user experience" in SML that I have missed. Because really, it seems kind of a lot of boilerplate in the way in order to use this in a code base.

wirrbel
  • 3,173
  • 3
  • 26
  • 49

1 Answers1

4

I guess Haskell performs something like this, too. Just implicitly.

The Haskell standard library defines and imports a ton of type class instances. Given a sufficient set of ML functors, their applications and their implicit compile-time imports, you could achieve something quite convenient.

But Haskell does let you automate type class instance declarations in ways that SML doesn't.

For example, instance Foo t => Bar t where ... is comparable to SML's higher-order functors, but in SML you explicitly have to generate a module that corresponds to Bar t for each concrete Foo t. Haskell also lets you derive instances syntactically.

OCaml had modular implicits from 2014 (example), but they mainly give you syntax sugar to refer to defined functor instances, rather than generate them.

I suspect that the reason ML module systems are still more explicit than Haskell's is because of things like overlapping instances.

Andreas Rossberg contributed 1ML in 2014 in which modules are first-class citizens. That means a function could take a module as an argument, e.g. like this:

;; Higher-kinded polymorphism

type MONAD (m : type => type) =
{
  return 'a : a -> m a;
  bind 'a 'b : m a -> (a -> m b) -> m b;
};

map 'a 'b (m : type => type) (M : MONAD m) (f : a -> b) mx =
  M.bind mx (fun x => M.return (f x));

do map :
  'a => 'b => (m : type => type) => (M : MONAD m) => (a -> b) -> m a -> m b;

This is still research-y in the sense that the compiler has "TOY" in its name, but it'd be an example of an ML (although not Standard ML) that does something comparably generic with modules.

sshine
  • 15,635
  • 1
  • 41
  • 66