An Example Syntax in Haskell, Erlang and Scala
>>> Updated Oct 16:
I found some conventions of coding style make code more readable for Scala. For example, use <pre>{ x => something } instead of (x => dosomething)</pre> <p>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.</br>
======
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?
![(please configure the [header_logo] section in trac.ini)](/chrome/site/blog_logo.png)
rss
Comments
Of the three, I am only familiar with Erlang, but Scala seems to be the most clear.
I could probably make that hunk of code in perpubplat more compact and idiomatic. The blog data model was the first part of that app that I wrote, and it was definitely early on in the learning process.
An Erlang version of the app could definitely benefit from some of the built-in middleware that Erlang includes, notably ETS and DETS.
really, all three are ugly as sin.
Don't say this is objective,it is fact.
I been professionally developing for 12 years and generally for 15
I've used a lot of crap.
I'll say this, scala is ok looking in syntax, ( I am used to groovy ) but case Nil.... Case _...
WTF
<p> I think a simpler coding would be in an OO style rather than a functional style. Which could be done in Scala anyway. Below is the code in Java. </p> <pre>