Crafting Code in F#

Howard Lewis wrote a nice post "Crafting Code in Clojure" about craftable code. He started with a rather ugly Java class and reached a lovely small Clojure solution. I was curious how it would be looked written in F#. Have a look at the result:

let sortedKeyList =
Seq.map (Map.toSeq >> Seq.map fst)
>> Seq.concat
>> Seq.distinct
>> Seq.sort
>> String.concat ", "
>> (function "" -> "<none>" | s -> s)
[<Fact>]
let ``Sorted key list``() =
[[], [], "<none>"
["fred", true], ["barney", true; "wilma", true], "barney, fred, wilma"
["fred", true], [], "fred"
["fred", true], ["fred", true; "barney", true], "barney, fred"]
|> List.iter (fun (m1, m2, res) -> sortedKeyList [Map.ofSeq m1; Map.ofSeq m2] =? res)
view raw gistfile1.fs hosted with ❤ by GitHub

It's odd that F# core library does not contain Map.keys/Map.values functions. Otherwise, the code would be a bit more shorter.

However, maps aren't used in static languages like F# to represent domain types. Hence, the more idiomatic version:

type Person = { Name: string; Flag: bool }
let sortedKeyList =
List.concat
>> List.map (fun { Name = name } -> name)
>> Seq.distinct
>> Seq.sort
>> String.concat ", "
>> (function "" -> "<none>" | s -> s)
[<Fact>]
let ``Sorted key list``() =
let persons = List.map (fun name -> { Name = name; Flag = true })
[
[], [], "<none>"
["fred"], ["barney"; "wilma"], "barney, fred, wilma"
["fred"], [], "fred"
["fred"], ["fred"; "barney"], "barney, fred"
]
|> List.iter (fun (a, b, res) -> sortedKeyList [a |> persons; b |> persons] =? res)
view raw gistfile1.fs hosted with ❤ by GitHub
(the Fact attribute is from xUnit.net library, and =? operator is from wonderful Unquote one).

Update 27.02.2018: Scala added

case class Person(name: String, flag: Boolean)
def sortedKeyList(xs: List[List[Person]]) = {
xs.flatten
.map(_.name)
.distinct
.sortBy(identity)
.mkString(", ") match {
case "" => "<none>"
case s => s
}
}
class Tests extends FunSuite with Matchers {
test("sorted key list") {
def people(xs: List[String]) = xs.map(Person(_, flag = true))
val data = List(
(Nil, Nil, "<none>"),
(List("fred"), List("barney", "wilma"), "barney, fred, wilma"),
(List("fred"), Nil, "fred"),
(List("fred"), List("fred", "barney"), "barney, fred"))
for ((a, b, res) <- data)
sortedKeyList(List(people(a), people(b))) should equal(res)
}
}

Very simple, very readable.

Comments

Popular posts from this blog

Regular expressions: Rust vs F# vs Scala

Hash maps: Rust, F#, D, Go, Scala

Haskell: performance