Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

polymorphism in haskell - using multiple versions of one function without giving it different names

the other day I wrote a small program to collect a bunch of numbers in a matrix - data Matrix = Matrix [[Int]] starting at a corner - Corner and follow a Path - [Direction] all of the three types are instance of a class Transformable

then i have a function that generates a transformation function, that turns a given corner and direction - to LeftUp and Right

turnLUR :: Transformable a => (Corner, Direction) -> a -> a

then i use this function to "harvest" all the numbers in the matrix:

harvest :: Matrix → [(Corner,Direction)] → [Int]
harvest _ [] = []
harvest (Matrix []) _ = []
harvest as (cd:csds) = b ++ harvest (Matrix bs) csds'  --cd = (Corner,Direction)
                     where f1 = turnLUR cd -- Matrix -> Matrix
                           f2 = turnLUR cd -- Corner -> Corner
                           f3 = turnLUR cd -- Direction -> Direction
                           Matrix (b:bs) = f1 as -- b = first line of [[Int]]
                           fcfd (c,d) = (f2 c,f3 d)
                           csds' = map fcfd csds

Now my question why do I have to write down f1, f2 and f3 instead of using one function f three times (keeping DRY in my mind!) - all three types Corners, Directions and Matrix are instances of the class Transformable.

How would I write that code without - making three versions of the "same" function?

like image 624
epsilonhalbe Avatar asked Feb 05 '12 16:02

epsilonhalbe


1 Answers

This is because of the monomorphism restriction. Give it an explicit type signature, and you'll be able to reuse the same function each time:

harvest :: Matrix → [(Corner,Direction)] → [Int]
harvest _ [] = []
harvest (Matrix []) _ = []
harvest as (cd:csds) = b ++ harvest (Matrix bs) csds'  --cd = (Corner,Direction)
                     where f :: Transformable a => a -> a
                           f = turnLUR cd
                           Matrix (b:bs) = f as -- b = first line of [[Int]]
                           fcfd (c,d) = (f c,f d)
                           csds' = map fcfd csds

Alternatively, you can turn it off by putting {-# LANGUAGE NoMonomorphismRestriction #-} at the top of your file.

The monomorphism restriction isn't well-liked — even GHC's manual refers to it as the dreaded monomorphism restriction — but there are some tricky corner cases, especially related to sharing (see the wiki page I linked for more information), that it avoids. In this case, I'd recommend just adding the type signature.

like image 110
ehird Avatar answered Sep 24 '22 14:09

ehird