Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the equivalent of OCaml's modules in Haskell?

Most of the Haskell code I see make use of direct structures such as lists and trees. For example, it is common for a Haskeller to write:

fillRect :: Color → Bounds → Image → Image

That pattern has a problem: if later on the programmer decides to modify the definition of "Image", or use another data-structure, then he will have to refactor every single piece of code using it. In OCaml, you can simply use a module specifying an interface to an Image, and then decide upon specific implementations later on.

What is the Haskell alternative to OCaml's modules?

like image 345
MaiaVictor Avatar asked Oct 07 '14 23:10

MaiaVictor


1 Answers

There are several alternatives; none exactly matches ML modules, but each some aspect.

  • Parametric polymorphism. Rather than parameterising your module and the fillRect function therein on Image, you parameterise the abstract Image type-constructor on something that specifies the particular "kind" of image. So that would be a signature like

    fillRect_ppm :: ImageImplemetation i => Colour -> Bounds -> Image i -> Image i
    

    where ImageImplemetation is some type class that specifies something like conversion and/or backend functions.
    With such a solution, you can't really replace the Image type completely, but you can make the type itself arbitrarily flexible. Likely, all concrete types you'll be using for Image will actually share some fields, so it's better not to make it more flexible than necessary: different instances of ImageImplemetation just need to implement what's different between them. If the implementations are very similar, perhaps you want to parameterise on just some detail like the colour space.
    Of course you need to plan ahead quite a bit with this solution, but just for a common interface. You can still put in any type as the argument later, provided you define the necessary instances.

  • Similarly (and possible to mix), you could just have a type class for types that can hold image data.

    fillRect_tcl :: Image img => Colour -> Bounds -> img -> img
    

    The Image class would directly define some primitives like how to draw lines, and fillRect_tcl would use these in its implementation. Good because fillRect_tcl works right away with any such type. What's a bit bad is that the Image class will need to be rather awkwardly big (it's preferred to use type classes for "mathematical" stuff with simple and very clear laws).

    • Perhaps the class could even have rectangles as a method:

      class Image img where
        ...
        fillRect_tcm :: Colour -> Bounds -> img -> img
        ...
      

      not so nice is, you'll need to completely re-define that method for any img instance.

  • Haskell's module system can't do a lot, but still it's sometimes enough to save you from refactoring everything. For instance, if Image came from a module Data.Image, you could have done

    import qualified Data.Image as IM
    
    fillRect_qfi :: Colour -> Bounds -> IM.Image -> IM.Image
    

    and use all functions defined in that module, also by qualifier. If at some point you decide to switch implementation, you can just change the import.
    Of course this isn't much good for switching between types on a regular basis, but quite ok for one-time changes.

Quite fitting to your example: diagrams uses both the first points; e.g. rect uses a TrailLike class to implement its primitives, and this class includes as "final" diagrams type QDiagram, which is parameterised on the backend you'll use for rendering.

like image 122
leftaroundabout Avatar answered Oct 22 '22 17:10

leftaroundabout