What is the equivalent of OCaml modules in Haskell?

Most of the Haskell code that I see uses straightforward structures like lists and trees. For example, Haskeller usually writes:

fillRect :: Color β†’ Bounds β†’ Image β†’ Image 

This template has a problem: if later the programmer decides to change the definition of "Image" or use a different data structure, then he will have to reorganize each piece of code using it . In OCaml, you can simply use the module that defines the interface for the image, and then later define specific implementations.

What is the Haskell alternative for OCaml modules?

+7
module haskell ocaml
source share
1 answer

There are several alternatives; none exactly match the ML modules, but each one.

  • Parametric polymorphism. Instead of parameterizing your module and the fillRect function fillRect in it, you parameterize the abstract Image -constructor type to indicate what a particular "view" of the image is. So this will be a type signature

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

    where ImageImplemetation is some type of class that indicates something like conversion functions and / or a backend.
    With this solution, you cannot completely replace the Image type, but you can make this type arbitrarily flexible. It is likely that all the specific types that you will use for Image will actually share some fields, so it’s better not to make it more flexible than necessary: ​​different instances of ImageImplemetation just have to implement what is different from them. If the implementations are very similar, perhaps you want to parameterize only some details, such as the color space. Of course, you need to plan this solution in advance, but only for a common interface. You can put any type as an argument later if you define the necessary instances.

  • Similarly (and possibly mix), you can simply have a type class for types that can hold image data.

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

    The Image class will directly define some primitives, for example, how to draw lines, and fillRect_tcl will use them in its implementation. Good, because fillRect_tcl works right away with any type. What's bad is that the Image class should be rather awkwardly large (he preferred to use type classes for "mathematical" material with simple and very clear laws).

    • Perhaps the class may have rectangles as a method:

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

      Not so nice, you need to completely override this method for any img instance.

  • Haskell's modular system cannot do much, but it is still sometimes enough to save you from reorganizing everything. For example, if Image came from a Data.Image module, you could do

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

    and use all the functions defined in this module, also using the qualifier. If at some point you decide to switch to implementation, you can just change the import.
    Of course, this is not very convenient for switching between types on a regular basis, but it is quite normal for one-time changes.

Compatible with your example: diagrams uses both first points; for example, rect uses the TrailLike class to implement its primitives, and this class includes the "final" diagrams of the QDiagram type, which is parameterized on the server, ll for rendering.

+10
source share

All Articles