Haskell exercises for beginners

July 17th, 2008

The exercises below are similar to my previous ‘Scala exercises for beginners’, except the rules a little clearer. For those of who have emailed me or submitted responses here on the blog, I will get around to providing you feedback, however, I’d prefer to do so on the revised version of the Exercises since then I can maintain a bit of clarity in explaining the solutions; specifically, why one may be preferred over another. I hope you don’t mind.

If you have not used Haskell before, download and install GHC, start up the interpreter (ghci) and load the source file. e.g. :load Exercises.hs If you are on Debian, you can install GHC and start the interpreter with: apt-get install ghc6 && ghci.

The source file below can be found here.

{-
The 'Scala exercises for beginners' set rules about what you were not permitted to use.
This is because you were permitted to use some functions, but not others. This led to
some amount of confusion (sorry). Instead, this time, I will define my own data types.
One is equivalent to Haskell's list [], although without the syntactic support and the
other is the set of natural numbers (0 onward). I will also predefine some useful
functions that you might consider using to solve each problem. That way, I needn't set
any rules; you are very much free to do what you want (Although, converting between
this type and [] is considered cheating, as you might expect ;) .

I will consider converting the Scala exercises to use a similar strategy in a second
revision to prevent this confusion.

TOTAL marks:    /66
-}

module Exercises where

import Prelude hiding (sum, length, map, filter, maximum, reverse, succ, pred)

-- The custom list type
data List t = Nil | Cons t (List t)

instance (Show t) => Show (List t) where
  show = show . toList
    where
    -- unfoldr, but let's not import Data.List
    toList Nil = []
    toList (Cons h t) = h : toList t

-- the custom numeric type
data Natural = Zero | Succ Natural
one = Succ Zero
two = Succ one

instance Show Natural where
  show = show . toInt
    where
    toInt Zero = 0
    toInt (Succ x) = 1 + toInt x

-- functions over Natural that you may consider using
succ :: Natural -> Natural
succ = Succ

pred :: Natural -> Natural
pred Zero = error "bzzt. Zero has no predecessor in naturals"
pred (Succ x) = x

-- functions over List that you may consider using
foldRight :: (a -> b -> b) -> b -> List a -> b
foldRight _ b Nil = b
foldRight f b (Cons h t) = f h (foldRight f b t)

foldLeft :: (b -> a -> b) -> b -> List a -> b
foldLeft _ b Nil = b
foldLeft f b (Cons h t) = let b' = f b h in b' `seq` foldLeft f b' t

reduceRight :: (a -> a -> a) -> List a -> a
reduceRight _ Nil = error "bzzt. reduceRight on empty list"
reduceRight f (Cons h t) = foldRight f h t

reduceLeft :: (a -> a -> a) -> List a -> a
reduceLeft _ Nil = error "bzzt. reduceLeft on empty list"
reduceLeft f (Cons h t) = foldLeft f h t

unfold :: (b -> Maybe (a, b)) -> b -> List a
unfold f b = case f b of Just (a, b') -> a `Cons` unfold f b'
                         Nothing -> Nil

-- Exercises

-- Exercise 1
-- Relative Difficulty: 1
-- Correctness: 2.0 marks
-- Performance: 0.5 mark
-- Elegance: 0.5 marks
-- Total: 3
add :: Natural -> Natural -> Natural
add = error "todo"

-- Exercise 2
-- Relative Difficulty: 2
-- Correctness: 2.5 marks
-- Performance: 1 mark
-- Elegance: 0.5 marks
-- Total: 4
sum :: List Int -> Int
sum = error "todo"

-- Exercise 3
-- Relative Difficulty: 2
-- Correctness: 2.5 marks
-- Performance: 1 mark
-- Elegance: 0.5 marks
-- Total: 4
length :: List a -> Int
length = error "todo"

-- Exercise 4
-- Relative Difficulty: 5
-- Correctness: 4.5 marks
-- Performance: 1.0 mark
-- Elegance: 1.5 marks
-- Total: 7
map :: (a -> b) -> List a -> List b
map = error "todo"

-- Exercise 5
-- Relative Difficulty: 5
-- Correctness: 4.5 marks
-- Performance: 1.5 marks
-- Elegance: 1 mark
-- Total: 7
filter :: (a -> Bool) -> List a -> List a
filter = error "todo"

-- Exercise 6
-- Relative Difficulty: 5
-- Correctness: 4.5 marks
-- Performance: 1.5 marks
-- Elegance: 1 mark
-- Total: 7
append :: List a -> List a -> List a
append = error "todo"

-- Exercise 7
-- Relative Difficulty: 5
-- Correctness: 4.5 marks
-- Performance: 1.5 marks
-- Elegance: 1 mark
-- Total: 7
flatten :: List (List a) -> List a
flatten = error "todo"

-- Exercise 8
-- Relative Difficulty: 7
-- Correctness: 5.0 marks
-- Performance: 1.5 marks
-- Elegance: 1.5 mark
-- Total: 8
flatMap :: List a -> (a -> List b) -> List b
flatMap = error "todo"

-- Exercise 9
-- Relative Difficulty: 8
-- Correctness: 3.5 marks
-- Performance: 3.0 marks
-- Elegance: 2.5 marks
-- Total: 9
maximum :: List Int -> Int
maximum = error "todo"

-- Exercise 10
-- Relative Difficulty: 10
-- Correctness: 5.0 marks
-- Performance: 2.5 marks
-- Elegance: 2.5 marks
-- Total: 10
reverse :: List a -> List a
reverse = error "todo"
Digg!

Scala exercises for beginners

July 15th, 2008

The following exercises have come from of a course that I give on Functional Programming. I have assigned them difficulty ratings to make it a bit more exciting. Download the compilable source code from here or find it below. Enjoy :)

These exercises can be translated into any language powerful enough to possess algebraic data types and case matching (yes, I am talking to you). Haskell is another such language and I will convert these exercises to Haskell if prompted.

// You are not permitted to use these List methods:
// * length
// * map
// * filter
// * ::: (and variations such as ++)
// * flatten
// * flatMap
// * reverse (and variations i.e. reverseMap, reverse_:::)
// This also means you are not permitted to use for-comprehensions on Lists.
// You are permitted to use the functions you write yourself. For example, Exercise 2 may use Exercise 1 or Exercise 3.
// Using permitted existing methods where appropriate will attract marks for elegance.

// TOTAL marks:    /66

object Exercises {
  def succ(n: Int) = n + 1
  def pred(n: Int) = n - 1

  // Exercise 1
  // Relative Difficulty: 1
  // Correctness: 2.0 marks
  // Performance: 0.5 mark
  // Elegance: 0.5 marks
  // Total: 3
  def add(x: Int, y: Int): Int = error("todo: Assume x and y are 0 or positive. Do not use + or - on Int. Only permitted to use succ/pred (above).")

  // Exercise 2
  // Relative Difficulty: 2
  // Correctness: 2.5 marks
  // Performance: 1 mark
  // Elegance: 0.5 marks
  // Total: 4
  def sum(x: List[Int]): Int = error("todo")

  // Exercise 3
  // Relative Difficulty: 2
  // Correctness: 2.5 marks
  // Performance: 1 mark
  // Elegance: 0.5 marks
  // Total: 4
  def length[A](x: List[A]): Int = error("todo")

  // Exercise 4
  // Relative Difficulty: 5
  // Correctness: 4.5 marks
  // Performance: 1.0 mark
  // Elegance: 1.5 marks
  // Total: 7
  def map[A, B](x: List[A], f: A => B): List[B] = error("todo")

  // Exercise 5
  // Relative Difficulty: 5
  // Correctness: 4.5 marks
  // Performance: 1.5 marks
  // Elegance: 1 mark
  // Total: 7
  def filter[A](x: List[A], f: A => Boolean): List[A] = error("todo")

  // Exercise 6
  // Relative Difficulty: 5
  // Correctness: 4.5 marks
  // Performance: 1.5 marks
  // Elegance: 1 mark
  // Total: 7
  def append[A](x: List[A], y: List[A]): List[A] = error("todo")

  // Exercise 7
  // Relative Difficulty: 5
  // Correctness: 4.5 marks
  // Performance: 1.5 marks
  // Elegance: 1 mark
  // Total: 7
  def concat[A](x: List[List[A]]): List[A] = error("todo")

  // Exercise 8
  // Relative Difficulty: 7
  // Correctness: 5.0 marks
  // Performance: 1.5 marks
  // Elegance: 1.5 mark
  // Total: 8
  def concatMap[A, B](x: List[A], f: A => List[B]): List[B] = error("todo")

  // Exercise 9
  // Relative Difficulty: 8
  // Correctness: 3.5 marks
  // Performance: 3.0 marks
  // Elegance: 2.5 marks
  // Total: 9
  def maximum(x: List[Int]): Int = error("todo")

  // Exercise 10
  // Relative Difficulty: 10
  // Correctness: 5.0 marks
  // Performance: 2.5 marks
  // Elegance: 2.5 marks
  // Total: 10
  def reverse[A](x: List[A]): List[A] = error("todo")
}
Digg!

Just an observation

July 7th, 2008

When some programmers pick up a functional programming language like Microsoft Excel, they stop caring about identifier names.

A1 = blah
B1 = function(A1, C2)
...

Can this observation be explained while maintaining consistency? I think it can.

Digg!

Implicits for the Fearless

July 5th, 2008

Some programmers are married to the imperative, side-effecting mindset. This makes them fearful of Scala’s implicit keyword (among many other high-level programming constructs and abstractions). You can read all sorts of amateurish criticism of this language construct on various websites, but I plan to show why they are a necessity to the language being useful (in the intellectually true and meaningful sense — not in a “Java” “pragmatist” sense (I know I’m treading on thin ice here, but the point is worthwhile)).

I also do not plan to address the amateurish claims specifically, since if you are like me, you simply acknowledge their existence, feel a bit sad, then get over it and move on. I mention these cases so that they can be contrasted to my plan to address a discussion I had with someone (who shall remain nameless for now and leave it up to them if they wish to reveal themselves) who put forward an intellectually constructed argument about why they can/should be eliminated. These kind of discussions are very purposeful. While my opponent’s argument was well thought-out (in contrast to the aforementioned), I still think it is wrong. Here is why.

You can do away with implicit definitions in favour of named functions with partial application. This is true, but is it useful? Furthermore, Scala is not exactly friendly to partially applying function arguments, requiring varying levels of awkwardness in certain contexts. I will assume, since I am a nice guy, that this is not the case. I will now alter the argument to what I consider to be equivalent, “Haskell can do away with type-classes”. I altered the argument simply for the sake of brevity in discussion — I will tie it back to Scala code examples at the end.

Let us assume we get rid of just one Haskell type-class, Monad. A monad is a useful abstraction, so we would certainly have to represent it (if you don’t believe this, then it is almost certain that you have reinvented monads inadvertently). We might do this as follows:

{-# LANGUAGE RankNTypes #-}

data Monad' m = Monad' {
  unit :: (forall a. a -> m a),
  bind :: (forall a b. m a -> (a -> m b) -> m b)
}

Then, when it comes to instances, we would write:

listMonad = Monad' (return :: a -> [a]) (>>=)
maybeMonad = Monad' (return :: a -> Maybe a) (>>=)

Now when we wish to write the all-important functions over monads, we have to do the following:

sequence' monad [] = unit monad []
sequence' monad (x:xs) = let (>>>=) = bind monad in x >>>= y -> sequence' monad xs >>>= ys -> unit monad (y:ys)

mapM' monad f as = ... etc. etc.

The proposal by my opponent in the discussion is that we now write a version of each monad function for each instance by partially applying the monad instance. This is very cumbersome and importantly, less useful.

listSequence = sequence' listMonad
maybeSequence = sequence' maybeMonad

listMapM = mapM' listMonad
... and so on.

This creates what would otherwise be code in the order of O(1) to turn into O(n) code! This is not good. Do we get any benefit? No, there is no benefit, other than, in the case of Scala, appeasement of ‘the irrational squad’ i.e. those fervently offended by the suggestion to divorce imperative programming. Let us just address this irrationality, even if just briefly, so we can move on.

Unlike Scala, Haskell’s side-effects are controlled, so the mind-shift is forced by the compiler (a successful form of counselling?). A Scala implicit function can perform side-effects. This would be very bad; if they were controlled, would these irrational objections be raised? The answer is no and it is quite easy to expose.

A claim to the contrary is often a case of doublethink. Java is a programming language that deliberately encourages side-effects and by implication (also, by its poor type system), makes abstraction and composition extremely difficult to the point of impossibility very early on (see Functional Java for evidence). Yet even in this language — perhaps the most contrived available — we find implicit conversions that attract no objections whatsoever!

Imagine this Java signature int toInt(char c). Such a user-defined function could indeed perform side-effects and any language feature permitting implicit use of this function is likely to attract criticism. Yet, this is one example of such a function built right-in to the Java language, which is guaranteed by the language specification to perform no side-effects. It is the fact that it is internalised by the user to perform no side-effects that makes it acceptable.

This is valid Java:

int i = c; // where c is of type char

I don’t wish to labour this point, other than to make it clear how easy it is to crush these irrationalities (it continues on in a usual uninteresting fashion).

Requiring code to blow out to O(n) from O(1) is itself not bad, but to further observe that there is no gain from it is conclusive. Interestingly, this is similar to a complaint by David MacIver (and myself — I am just less vocal about it) that Scala lacks default function argument lists in preference for overloading. This also creates unnecessary code-bloat (O(1) -> O(n)) for no benefit. (I hope I am not misrepresenting David here — I have lost the reference to his complaint).

Here is the equivalent Scala code to the above, however, Scala’s type system lacks the ability to pass the unit and bind functions — instead requiring the use of traits.

trait Unit[U[_]] {
  def unit[A](a: A): U[A]
}

trait Bind[M[_]] {
  def bind[A, B](ma: M[A], f: A => M[B]): M[B]
}

case class Monad[M[_]](u: Unit[M], b: Bind[M])

object Monad {
  val listMonad = Monad[List](new Unit[List] {
    def unit[A](a: A) = List(a) }, new Bind[List] {
    def bind[A, B](ma: List[A], f: A => List[B]) = ma flatMap f
  })

  def sequence[M[_], A](m: Monad[M], as: List[M[A]]): M[List[A]] = as match {
    case Nil => m.u.unit(Nil)
    case x :: xs => m.b.bind(x, (y: A) => m.b.bind(sequence[M, A](m, xs), (ys: List[A]) => m.u.unit(y::ys)))
  }

  def listSequence[A](as: List[List[A]]) = sequence[List, A](listMonad, as)
  def optionSequence[A](as: List[List[A]]) = sequence[Option, A](optionMonad, as)
  // etc. etc. O(n)!
}

As you can see, I was being very nice when I converted the argument to Haskell. The argument against such a proposal becomes even stronger in the context of Scala.

To my opponent, thanks for the discussion; it was fun and the intellectual maturity is admirable :)

Digg!

Applicative Functor laws using Reductio (Scala)

July 3rd, 2008

Here is the Applicative functor type-class (see Applicative Programming with Effects, Conor McBride, Ross Paterson):

class Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

Here are the laws for the Applicative functor type-class:

  • identity
    pure id <*> u == u
  • composition
    pure (.) <*> u <*> v <*> w == u <*> (v <*> w)
  • homomorphism
    pure f <*> pure x == pure (f x)
  • interchange
    u <*> pure x == pure (\f -> f x) <*> u

Here are those laws stated using Reductio. I have changed pure to unit and the language is Scala:


object ApplicativeLaws {
  def identity[A[_], X](implicit a: Applicative[A],
                                 aax: Arbitrary[A[X]],
                                 e: Equal[A[X]]) =
    prop((apx: A[X]) => apx === a(a.unit((x: X) => x), apx))

  def composition[A[_], X, Y, Z](implicit a: Applicative[A],
                                aayz: Arbitrary[A[Y => Z]],
                                aaxy: Arbitrary[A[X => Y]],
                                aax: Arbitrary[A[X]],
                                e: Equal[A[Z]]) =
    prop((apyz: A[Y => Z], apxy: A[X => Y], apx: A[X]) =>
            a(apyz, a(apxy, apx)) ===
            a(a(a(a.unit((f: Y => Z) => (g: X => Y) => f compose g), apyz), apxy), apx))

  def homomorphism[A[_], X, Y](implicit a: Applicative[A],
                                        ax: Arbitrary[X],
                                        axy: Arbitrary[X => Y],
                                        e: Equal[A[Y]]) =
    prop((f: X => Y, x: X) => a(a.unit(f), a.unit(x)) === a.unit(f(x)))

  def interchange[A[_], X, Y](implicit a: Applicative[A],
                                       ax: Arbitrary[X],
                                       apxy: Arbitrary[A[X => Y]],
                                       e: Equal[A[Y]]) =
    prop((f: A[X => Y], x: X) =>
      a(f, a.unit(x)) === a(a.unit((f: X => Y) => f(x)), f))
}

Pretty neat eh? :) Let’s test the Applicative instance for List:

List(identity[List, Int],
     composition[List, Int, Long, String],
     homomorphism[List, Int, String],
     interchange[List, Int, String]).
  foreach(p => summary println p)

This runs 100 unit tests per property, so 400 unit tests altogether. Here is the output:

OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.

Woot woot!

Digg!

Just what the funk is a Functor anyway?

June 28th, 2008

Runar recently made mention of these so-called Functors and Monads in his excellent post about concurrency/actors in Java. There are all sorts of tutorials out there for understanding what a Monad is, however, I am of the opinion that one must first understand what a Functor is. This is because it is less complex and more general (all monads are functors plus more).

The Java Posse also made a couple of false claims about Runar’s article, but one in particular needs addressing. This is the notion that a functor is applicable to “Functional Programming”. This is false. A functor is applicable to programming. You really, really need to understand what a functor is, if you’re ever going to be skilled in any type of programming; even if it’s just Java web applications for the rest of your life. This concept does not exist ‘on the other side of the fence’ in the imaginary ‘functional programming world’. These suggestions are understandable of course and I don’t intend to labour the point — only make this point clear, since these apparent divides seem to pop up often. On with the story…

In Java, we have an interface called CharSequence. A few things can be said about CharSequence. How about:

All implementations guarantee that for some integer n, then if n >= 0 && n < length(), then charAt(n) will not throw an exception.

We might call this a law about CharSequence that all implementations must satisfy. We could even find other laws that must satisfy about implementations of the CharSequence interface.

A Functor is an interface and it has two laws. Sadly though, this interface cannot be expressed using Java’s type system — it is simply not clever enough. We must use a hypothetical Java to express it. First though, we have to make up for Java’s missing first-class functions by using an interface. This is easy:

interface Function<A, B> {
  B f(A a);
}

OK, so this a just an interface like any others. It needn’t satisfy any laws; it just takes an argument (A) and returns (B). We could write bazillions of implementations of this interface in Java. We’re going to need this interface to write the Functor interface.

Now, the missing part of Java is called higher-kinds — a term you needn’t concern yourself with too much. I hope this hypothetical syntax is enough to make the point clear:

interface Functor<E<_>> {
  <A, B> E<B> fmap(Function<A, B> f, E<A> fa);
}

So the foreign part here is E<_>. Consider E to stand for ‘Environment’. It is a type parameter that takes yet another type parameter (which is why it is denoted E<_>). This means I could use List or Set, but not Integer as the type parameter value for E, since List and Set take another type parameter while Integer does not. You might think of the Functor interface as ‘applying the given function within the environment’.

We might write an implementation like this:

class ListFunctor implements Functor<List> {
  public <A, B> List<B> fmap(Function<A, B> f, List<A> list) {
    // todo: apply the given function on all elements in 'list' and return the result
  }
}

In general discussion, we would call this the list functor, simply because the value for the environment has been applied. In Runar’s article, he was talking about the Callable functor; yet another environment. There are many possibilities for the environment here; List and Callable are but two.

The Laws

Let us not forget the laws :) Just like all implementations of CharSequence have laws to obey, so do all functors. They are called identity and composition.

Identity

Remember the Function interface? Here is an implementation:

class Identity<A> implements Function<A, A> {
  public A f(A a) { return a; }
}

If we are given an implementation of Functor — which I will call f — then it must satisfy f.fmap(new Identity<T>(), x) is equivalent to x (for any value of x). So that’s the identity law out of the way. Mapping the identity function across a functor yields the same value.

Composition

The law of composition is a little less friendly when it comes to expressing it in Java so I will refrain :) While it is an important part of making up the concept of a functor, it may be best to defer this final piece of the puzzle for now.

For completeness, I will state the law using a shorter syntax, but if this is foreign or worrying, then please ignore it: f.fmap(a compose b, x) is equivalent to f.fmap(a, f.fmap(b, x)) (for any value of a, b and x — a and b are Function objects).

That’s all there is to a Functor. If you use the map function on lists or perhaps the map method on scala.Option, then you are using one specific instance of a functor, since these functions/methods satisfy the conditions for a functor. Even throwing an exception is using a specific functor! Perhaps that can be explained another time :)

I should also add that when referring to a functor (unqualified), we are referring to a covariant functor. Other types of functors include contra-variant and invariant functors. We can talk about those another day :)

Next: Just What the Flip is a Monad?

Questions?

Digg!

Monad Laws using Reductio (Scala)

June 26th, 2008

In the spirit of yesterday’s post that denoted the Functor Laws using Reductio, I will also give the Monad Laws. Before I do however, here is an example use of the FunctorLaws object that runs 600 unit tests when testing a Functor implementation for scala.Option[A], scala.List and finally, for the scala.Either[A, _] functor just to make it a bit quirky and probably confuse a few of you (in that case, ignore it for now!) :)

  val prop_OptionIdentity = identity[Option, Int]
  val prop_OptionComposition = composition[Option, Int, String, Long]

  val prop_ListIdentity = identity[List, Int]
  val prop_ListComposition = composition[List, Int, String, Long]

  val prop_EitherIdentity = identity[Apply1Of2[Either, String]#Apply, Int]
  val prop_EitherComposition = composition[Apply1Of2[Either, String]#Apply, Int, String, Long]

  // OK passed 100 tests. (appears 6 times — once per property)

…and the obligatory Monad Laws follow (import statements omitted this time, but they are the same as previous)…

object MonadLaws {
  def leftIdentity[M[_], A, B](implicit m: Monad[M],
                                     am: Arbitrary[M[B]],
                                     aa: Arbitrary[A],
                                     ca: Coarbitrary[A]) =
    prop((a: A, f: A => M[B]) =>
      m.bind(f, m.unit(a)) === f(a))

  def rightIdentity[M[_], A](implicit m: Monad[M],
                                      am: Arbitrary[M[A]]) =
    prop((ma: M[A]) => m.bind((a: A) =>
      m.unit(a), ma) === ma)

  def associativity[M[_], A, B, C](implicit m: Monad[M],
                                           am: Arbitrary[M[A]],
                                           ca: Coarbitrary[A],
                                           cb: Coarbitrary[B],
                                           amb: Arbitrary[M[B]],
                                           amc: Arbitrary[M[C]]) =
    prop((ma: M[A], f: A => M[B], g: B => M[C]) =>
      m.bind(g, m.bind(f, ma)) === m.bind((a: A) => m.bind(g, f(a)), ma))
}

Woot! Woot!

Digg!

Functor Laws using Reductio (Scala)

June 25th, 2008

A somewhat intricate and extremely useful code snippet using Reductio for testing the laws of any Functor instance (note that the Functor type is part of Scalaz):

import reductios.Property._
import reductios.Arbitrary
import reductio.Coarbitrary
import reductios.Arbitrary._

object FunctorLaws {
  def identity[F[_], A](implicit f: Functor[F], af: Arbitrary[F[A]]) =
    prop((fa: F[A]) => f.fmap((a: A) => a, fa) === fa)

  def composition[F[_], A, B, C](implicit f: Functor[F],
                                          af: Arbitrary[F[A]],
                                          ac: Arbitrary[C],
                                          cb: Coarbitrary[B],
                                          ab: Arbitrary[B],
                                          ca: Coarbitrary[A]) =
    prop((fa: F[A], h: B => C, i: A => B) =>
            f.fmap(h compose i, fa) === f.fmap(h, f.fmap(i, fa)))
}
Digg!

You’d naturally write flatMap yourself if asked the question

June 25th, 2008

Many people struggle to understand those fluffy things called Monads and why they are important. I’m not going to attempt to alleviate this to a large degree, but I have had a recent success with a friend in having them attempt to write a familiar Scala function as a method. That is, the List.flatten function, which simply takes a List of List of some type A and returns a List[A]. It does this by concatenating all the lists together. I think most people are familiar with this function and have a good mental model of how it works. If this is the case and you are less confident about List.flatMap, then I hope to bring a point to your attention that might help you bring it home.

If you take a look at List.sort, you see it takes a function (A, A) => Boolean. This is because the type parameter to List is ‘just any A’. It would be nice if it was ‘any A, so long as it has defined order’. That way, you can just call list.sort and be done with it. This would require the creation of a new class with a more restricted type parameter; for example, you might pass the function at list creation time, like you do with a TreeMap.

Imagine writing List.flatten on the List[A] class as a method using the same technique. You wouldn’t be able to, since the method belongs on a List[List[A]]. You’d need to write the method such that it takes an argument: A => List[A] before you could then call flatten. When you have a List[List[A]], you’d just call this method with the identity function x => x to obtain your resulting List[A]. Here is how the method would look:

def flatten(f: A => List[A]): List[A] = ...

Guess what?! This method is flatMap! Let’s ignore the fact that the Scala API is significantly broken, including the List.flatMap method using Iterable in place of List here. Notice though, that flatMap is actually generalised by taking another type parameter, so we have just invented an unnecessarily specialised version of flatMap. Let’s fix it:

def flatten[B](f: A => List[B]): List[B] = ...

Woot woot! In other words, flatten(list) is equivalent to list flatMap (x => x). Furthermore, this should hold for more than just list, but for other type constructors too (sadly, Option is missing a flatten function: Option[Option[A]] => Option[A]). This is a special relationship that all monads have (join is a synonym for flatten and bind is a synonym for flatMap):

join = bind id

We can express this using Reductio (the Scala API of course!):

val prop_flat = prop((t: List[List[Int]]) => List.flatten(t) == t.flatMap(x => x))
// OK passed 100 tests.

I hope this helps. If it doesn’t, ask a question or ignore my ranting :)

Digg!

ABC Learning Centres required for adults

June 23rd, 2008

From http://www.news.com.au/comments/0,23600,23907216-462,00.html on the recent price rise by ABC Learning Centres (i.e. child care):

ABC Learing centres have now increased there fees three times since early 2007. There justification for these raises in no way benefit the children in they’re care or they’re staff!.

Digg!