Friday, August 30, 2013

How to Overload functions in Haskell

Haskell is a difficult language to get coming from imperative languages. In languages like C++ and Java, we can say this:

addOne(int x){ return x + 1;}
addOne(double x) { return x + 1.0;}

Here, addOne works on different types, but is still the same function name. This is called Overloading, and it's somewhat more difficult to do in Haskell. In order to overload functions, you will need to define a Typeclass (With the FlexibleInstances Program) to work on the types you want. You can indeed get this to work on generics.

To get this to work, we'll use an example. Think of the function "after". If you said after "profectium" 'f' , you would expect to get "ectium". The type signature for this function is after :: (Eq a) => [a] -> a -> [a]. However, there are cases where you may supply a list to the second argument of after, like in this example: after "profectium" "pro" in order to get the same output. This last example could be useful for sanitation purposes, and to avoid false positives.

The typeclass to get these two functions work will be called "Continuous". Here is the class definition: 

class Continuous list part where
  after :: list -> part -> list


In both instances of after, you take two arguments and return a sublist of a greater argument. So in order to become part of class Continuous, they have to satisfy an implementation of this function. To make out lives easier, we can use generics instead of individual types in our instance declarations. Everything that is part of the Eq typeclass will automatically qualify for class Continuous.

Here are some helper functions, ignore them if you're unconcerned with implementation specifics: 
sub :: (Eq a) => [a] -> Int -> [a]
sub [] _ = []
sub x 0 = x
sub (x:xs) c = sub xs (c - 1)

pos :: (Eq a) => [a] -> a -> Int
pos [] _ = 0
pos (x:xs) c
  | x == c = 0
  | otherwise = 1 + pos xs c



These will be used to define the after functions. The two headers for after are:


instance (Eq a) => Continuous [a] a
instance (Eq a) => Continuous [a] [a]


This is barring the implementations. Because of the requirements pos (and, by proxy, sub) to have the Eq typeclass, we constrain it in the instance declaration. We also set this up to work with Generics. This will now work. The full file text for the After library is as follows: 

{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Cookbook.Continuous(
sub :: (Eq a) => [a] -> Int -> [a]
sub [] _ = []
sub x 0 = x
sub (x:xs) c = sub xs (c - 1)

pos :: (Eq a) => [a] -> a -> Int
pos [] _ = 0
pos (x:xs) c
  | x == c = 0
  | otherwise = 1 + pos xs c


class Continuous list part where
  after :: list -> part -> list

instance (Eq a) => Continuous [a] a where
  after x c = sub x (1+(pos x c))

instance (Eq a) => Continuous [a] [a] where
  after [] _ = []
  after x c
    | take (length c) x == c = sub x ((length c))
    | otherwise = after (tail x) c


Note the language pragmas at the top. They are required for this to work. Pragmas just enable features of ghc that are turned off by default.
 

No comments:

Post a Comment