First, we compared the classic imperative paradigm, in which we dictate what the computer must do:
- explicit iteration (loops)
- mutable variables (watch for surprises: should use "final")
- implicit iteration (let the JVM work out the best way to iterate depending on the hardware)
- immutable variables (harder to get it wrong).
- Example-based testing: x -> !; check for (0, 1), (1, 1), (7, x5040) etc.
- Property-based testing: n=0, f(n)=1; n>0, f(n)=n*f(n-1) (Test uses recursion...). Examples are generated randomly by the JVM.
fizzbuzz = iterator 100 []
where iterator 0 a = reverse a
iterator i a = iterator(i-1)(a ++ [fb i]
where fb x = if x mod 15 == 0 then "fizzbuzz"
else if x mod 5 == 0 then "buzz"
else if x mod 3 == 0 then "fizz"
else show x
This works, but the fb function is basically Fortran and we are explicitly iterating. In a functional paradigm, this becomes:
choose x = if x mod 15 == 0 then "fizzbuzz"
else if x mod 5 == 0 then "buzz"
else if x mod 3 == 0 then "fizz"
else show x
fizzbuzz = map choose [1..100]
main = println fizzbuzz
So the map function applies the choose algorithm to each element of the list (implicit iteration) However, we still have a Fortrany block in the choose method. Luckily, we have more functions:
fizzes = cycle ["", "", "fizz"]
buzzes = cycle ["", "", "", "", "buzz"]
fizzbuzzes = zipWith (++) fizzes buzzes
result = zipWith chooser fizzbuzzes [1..]
where chooser s n = if s != "" then s
else show n
main = println (take 100 result)
The cycle function returns an infinite list that cycles among the elements. zipWith interleaves two lists into a list of 2-tuples. chooser takes each 2-tuple off the list and tests its elements.
- fizzes gives us "", "", fizz, "", "", fizz, etc.
- buzzes gives "", "", "", "", buzz, "", etc.
- fizzbuzzes interleaves the fizzes and buzzes ("", ""), ("",""), (fizz, ""), ("",""), ("",buzz), etc.
- result interleaves numbers with the fizzbuzzes, but uses the chooser function to decide what to select - if there's a word print it, else print the number.
The next part of the workshop consisted of translating the solution above into Java. The key feature used was the Stream Interface, which allows us to connect to the fizzes and buzzes lists without instantiating them. The StreamUtils package provided the zipWith function and iteration was avoided by implementing the map-reduce pattern.
Finally, we embarked on yet another functional language, Kotlin. Actually, it turns out this one has procedural and object-oriented elements to it. Chatting to Brian later, I mentioned that I was beginning to suffer from language-fatigue, as yet another new language becomes the flavour of the month. What was wrong with Scala, I asked. His reply was that Scala was the first-pass at an enterprise-grade functional language. He thought it a solid effort, but feared that it had some basic design flaws that will limit it eventually. In his opinion, Kotlin is the real deal...
No comments:
Post a Comment