An Example Syntax in Haskell, Erlang and Scala

Caoyuan Blog - - October 15, 2008

>>> Updated Oct 16:

I found some conventions of coding style make code more readable for Scala. For example, use

{ x => something } instead of (x => dosomething)

for anonymous function; Use x, y, z as the names of arguments of anonymous functions; Put all modifiers to the ahead line etc. That makes me can pick up these functions by eyes quickly. ======

It’s actually my first time to write true Scala code, sounds strange? Before I write Scala code, I wrote a Scala IDE first, and am a bit familiar with Scala syntax now. And I’ve got about 1.5 year experience on Erlang, it began after I wrote ErlyBird.

Now it’s time to write some real world Scala code, I choose to port Paul R. Brown’s perpubplat blog engine, which is written in Haskell. And I have also some curiosities on how the syntax looks in Erlang, so I tried some Erlang code too.

Here’s some code piece of entry module in Haskell, Erlang and Scala:

Original Haskell code piece

empty :: Model
empty = Model M.empty M.empty M.empty [] 0 build_model :: [Item] -> Model
build_model [] = empty
build_model items = Model (map_by permatitle sorted_items) bid (build_child_map sorted_items) (sorted_items) (n+1) where sorted_items = sort_by_created_reverse items bid = (map_by internal_id sorted_items) n = fst . M.findMax $ bid build_child_map :: [Item] -> M.Map Int [Int]
build_child_map i = build_child_map_ (M.fromList $ (map (\x -> (internal_id x,[])) i)) i -- Constructed to take advantage of the input being in sorted order.
build_child_map_ :: M.Map Int [Int] -> [Item] -> M.Map Int [Int]
build_child_map_ m [] = m
build_child_map_ m (i:is) = if (parent i == Nothing) then build_child_map_ m is else build_child_map_ (M.insertWith (++) (unwrap $ parent i) [internal_id i] m) is sort_by_created_reverse :: [Item] -> [Item]
sort_by_created_reverse = sortBy created_sort_reverse created_sort_reverse :: Item -> Item -> Ordering
created_sort_reverse a b = compare (created b) (created a)

In Erlang:

% @spec empty :: Model
empty() -> #model{}. % @spec build_model :: [Item] -> Model
build_model([]) -> empty();
build_model(Is) -> SortedIs = sort_by_created_reverse(Is), Bid = dict:from_list([{I#item.internal_id, I} || I <- SortedIs]), N = lists:max(dict:fetch_keys(Bid)), #model{by_permatitle = dict:from_list([{X#item.permatitle, X} || X <- SortedIs]), by_int_id = Bid, child_map = build_child_map(SortedIs), all_items = SortedIs, next_id = N + 1}. % @spec build_child_map :: [Item] -> M.Map Int [Int]
build_child_map(Is) -> build_child_map_(dict:from_list(lists:map(fun (X) -> {X#item.internal_id, []} end), Is), Is). %% Constructed to take advantage of the input being in sorted order.
% @spec build_child_map_ :: M.Map Int [Int] -> [Item] -> M.Map Int [Int]
build_child_map_(D, []) -> D;
build_child_map_(D, [I|Is]) -> case I#item.parent of undefined -> build_child_map_(D, Is); P_Id -> build_child_map_(dict:append(unwrap(P_Id), I#item.internal_id, D), Is) end. % @spec sort_by_created_reverse :: [Item] -> [Item]
sort_by_created_reverse(Is) -> lists:sort(fun created_sort_reverse/2, Is). % @spec created_sort_reverse :: Item -> Item -> Ordering
created_sort_reverse(A, B) -> compare(B#item.created, A#item.created).

In Scala

object Entry { def empty = new Model() def build_model(is:List[Item]) = is match { case Nil => empty case _ => val sortedIs = sortByCreatedReverse(is) val bid = Map() ++ sortedIs.map{ x => (x.internalId -> x) } val n = bid.keys.toList.sort{ (x, y) => x > y }.head // max new Model(Map() ++ sortedIs.map{ x => (x.permatitle -> x) }, bid, buildChildMap(sortedIs), sortedIs, n + 1) } def buildChildMap(is:List[Item]) = buildChildMap_(Map() ++ is.map{ x => (x.internalId -> Nil) }, is) private def buildChildMap_(map:Map[Int, List[Int]], is:List[Item]) = { map ++ (for (i <- is if i.parent.isDefined; pid = i.parent.get; cids = map.getOrElse(pid, Nil)) yield (pid -> (cids + i.internalId))) } def sortByCreatedReverse(is:List[Item]) = is.sort{ (x, y) => x.created before y.created }
}

>>> Updated Oct 16: Per Martin’s suggestion, the above code can be written more Scala style (the reasons are in the comments). Thanks, Martin.

object Entry { def empty = new Model() def build_model(is:List[Item]) = is match { case Nil => empty case _ => val sortedIs = sortByCreatedReverse(is) val bid = Map() ++ sortedIs.map{ x => (x.internalId -> x) } // use predefined max in Iterable val n = Iterable.max(bid.keys.toList) new Model(Map() ++ sortedIs.map{ x => (x.permatitle -> x) }, bid, buildChildMap(sortedIs), sortedIs, n + 1) } // you can use a wildcard anonymousfunction here def buildChildMap(is:List[Item]) = buildChildMap_(Map() ++ is.map(_.internalId -> Nil), is) private def buildChildMap_(map:Map[Int, List[Int]], is:List[Item]) = map ++ { // rewrite for so that definitions go into body -- it's more efficient. for (i <- is if i.parent.isDefined) yield { val pid = i.parent.get val cids = map.getOrElse(pid, Nil) pid -> (cids + i.internalId) } } // you can use a wildcard anonymous function here def sortByCreatedReverse(is:List[Item]) = is.sort{ _.created before _.created } }

======

I use ErlyBird for Erlang coding, and Scala for NetBeans for Scala coding. The experience seems that IDE is much aware of Scala, and I can get the typing a bit faster than writing Erlang code.

If you are not familiar with all these 3 languages, which one looks more understandable?



Categories: Blogs  Caoyuan Blog  

Comments

No comments so far, you could be the first.

Add comment

Name:

Email:

URL:

Smileys

Remember my personal information

Notify me of follow-up comments?