Skip to content

Commit 1c43631

Browse files
committed
arnaud learns scala part 1
1 parent 609b13e commit 1c43631

File tree

4 files changed

+307
-1
lines changed

4 files changed

+307
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
---
2+
layout: post
3+
title: Arnaud learns Scala - part 1
4+
---
5+
6+
After having been a C++ developer for years, I started coding in java. But that was 10 years ago
7+
and, although I am now a seasoned java developer, I feel kind of outdated when I see how functional
8+
programming became more than a fashion. Switching entirely to functional programming would be quite
9+
a big move but a language like scala claims to make object-oriented programming meet functional
10+
programming... So I decided to learn scala, I'm 10 years late but it's never too late!
11+
12+
![The Scala Programming Language]({{ site.url }}/assets/scala-lang.org/smooth-spiral.png){: .arnodb-center-image }
13+
14+
More than learning scala because everyone knows scala, this decision aims at getting familiar with
15+
real life functional programming. Morover a lot of scala developers claim it greatly improved the
16+
quality of their object-oriented code and I believe them.
17+
18+
Entering the Scala world
19+
------------------------
20+
21+
Well, this is the simplest thing to so, scala has a website dedicated to itself:
22+
[The Scala Programming Language](http://www.scala-lang.org/) (http://www.scala-lang.org/). It
23+
provides a huge amount of links to various documentation.
24+
25+
I started with the obvious [Getting
26+
Started](http://www.scala-lang.org/documentation/getting-started.html) document and made the *Hello
27+
World* program run properly. By the way, I am a linux user (this is not definitive ;-) ), more
28+
precisely I am a Debian user, so `apt-get install scala` was enough to start coding in scala.
29+
30+
The computer I used is not very powerful, this is why I haven't installed any IDE yet. Fortunately
31+
scala can be scripted:
32+
33+
{% highlight scala %}
34+
#!/bin/sh
35+
exec scala "$0" "$@"
36+
!#
37+
// Scala script
38+
...
39+
{% endhighlight %}
40+
41+
Then I was unsure where to go next. I found [A Scala Tutorial for Java
42+
Programmers](http://docs.scala-lang.org/tutorials/scala-for-java-programmers.html). It is really
43+
worth a reading.
44+
45+
Then I was eager to code a bit although I didn't know a lot.
46+
47+
Tower of Hanoi
48+
--------------
49+
50+
This game is well known and I chose it to start my experiments (one would be surprised by the
51+
variety of tools allowing the implementation of Tower of Hanoi :-) ). It took me 2 days and a lot of
52+
stackoverflow pages) to write this small scala script:
53+
[hanoi-1.scala
54+
(GitHub)](https://github.com/arnodb/arnodb.learns.scala/blob/master/sandbox/hanoi-1.scala). A
55+
seasoned scala developer may find it extremely poor, not efficient etc. But I learned a lot with it.
56+
57+
### Tower class ###
58+
59+
{% highlight scala %}
60+
class Tower(val floors: ArrayStack[Int]) {
61+
62+
def height = floors.size
63+
64+
override def toString() = toStrings(24).mkString("\n")
65+
66+
def floorSizeToString(s: Int, width: Int) = {
67+
val side = width / 2 - s
68+
(" " * side) + ("-" * (s * 2)) + (" " * side)
69+
}
70+
71+
def floorToString(f: Int, width: Int) = {
72+
if(f < floors.size) floorSizeToString(floors(floors.size - f - 1), width) else (" " * width)
73+
}
74+
75+
def toStrings(width: Int) = {
76+
floors.map(f => floorSizeToString(f, width))
77+
}
78+
79+
}
80+
{% endhighlight %}
81+
82+
* the `val` keyword in front of `floors` automatically defines a getter, otherwise `myTower.floors` would not
83+
work
84+
* the `height` accessor is here to add a bit of semantic: the height of a tower is the number of
85+
floors
86+
* the rest is dedicated to generate a human readable representation of the tower, it helped me learn
87+
* how to join a list of strings (`mkString`)
88+
* how to repeat a string multiple times (`"..." * repeat `)
89+
* a simple map call, the first bit of functional programming (yeah!)
90+
91+
### Tower companion ###
92+
93+
{% highlight scala %}
94+
object Tower {
95+
96+
def foundation() = new Tower(new ArrayStack)
97+
98+
def complete(bottom: Int) = range(bottom, 1)
99+
100+
private def range(bottom: Int, top: Int) = new Tower(ArrayStack.range(top, bottom + 1))
101+
102+
}
103+
{% endhighlight %}
104+
105+
I was a bit lazy but apparently the *companion* object is the pattern to use in scala in order to
106+
create factory methods. My Tower companion allows to create a tower with no floor at all
107+
(`foundation`) or a tower fully built with the first floor specified as argument and the last one of
108+
size 1 (`complete`).
109+
110+
For the java programmers: a companion is kind of a class with static methods. Actually it is a
111+
singleton but that makes no difference.
112+
113+
Technically a companion has access to private members, that may be useful. Moreover factory methods
114+
in a companion do not require the `new` keyword whereas constructors do. Syntactic sugar...
115+
116+
### City class and companion ###
117+
118+
{% highlight scala %}
119+
class City(val towers: Array[Tower]) {
120+
121+
def size = towers.size
122+
123+
override def toString() = {
124+
val cityHeight = 12
125+
// from top to bottom
126+
(for(level <- cityHeight - 1 to 0 by -1) yield {
127+
(for(tower <- towers) yield {
128+
tower.floorToString(level, 24)
129+
}).mkString(" ")
130+
}).mkString("\n") +"\n" + "^" * (24 * 3 + 2)
131+
}
132+
133+
}
134+
135+
object City {
136+
137+
def apply(towers: Tower*) = new City(towers.toArray)
138+
139+
}
140+
{% endhighlight %}
141+
142+
Two points:
143+
144+
* `yield` invokes the map function, very usefull!
145+
* `apply` allows the following syntax: `City(...)` instead of `City.factory(...)`, very useful!
146+
147+
### Crane ###
148+
149+
Then we need a crane to move one floor from one tower to another:
150+
151+
{% highlight scala %}
152+
object Crane {
153+
154+
def move(city: City, from: Int, to: Int) {
155+
val fromTower = city.towers(from)
156+
val toTower = city.towers(to)
157+
if(toTower accepts fromTower) {
158+
toTower.floors push fromTower.floors.pop
159+
} else {
160+
println("Beep! Tower " + (to + 1) + " cannot receive the top of tower " + (from + 1))
161+
}
162+
}
163+
164+
}
165+
{% endhighlight %}
166+
167+
Its purpose is only to check that the destination tower accepts the top floor of the source tower.
168+
Note the infixed call to `Tower.accept` and `ArrayStack.push`. The `accepts` method is added to the
169+
`Tower` class:
170+
171+
{% highlight scala %}
172+
class Tower {
173+
174+
def accepts(from: Tower) = {
175+
from.floors.size > 0 && (floors.size == 0 || from.floors.top < floors.top)
176+
}
177+
178+
}
179+
{% endhighlight %}
180+
181+
### Movement algorithm, the functional way ###
182+
183+
Then comes the algorithm to move the floors from one tower to another. A very silly one that
184+
recursively moves "all but the first floor" to the spare slot, moves "the first floor" to the
185+
destination, and repeat the first operation to rebuild the top of the tower.
186+
187+
188+
{% highlight scala %}
189+
object Move {
190+
191+
def superMove(height: Int, from: Int, to: Int, move: (Int, Int) => Unit): () => Unit = {
192+
height match {
193+
case 0 => () => ()
194+
case 1 => () => move(from, to)
195+
case _ => {
196+
val s = Set(0, 1, 2)
197+
s -= from
198+
s -= to
199+
() => {
200+
superMove(height - 1, from, s.head, move)()
201+
move(from, to)
202+
superMove(height - 1, s.head, to, move)()
203+
}
204+
}
205+
}
206+
}
207+
208+
}
209+
{% endhighlight %}
210+
211+
* `superMove` aims at moving a given amount of floors from one tower to another
212+
* it takes an elementary move callback whose prototype is defined by `(Int, Int) => Unit`, basically
213+
a function that takes two integers
214+
* it returns a function which we have to execute in order to perform the operation
215+
216+
The algorithm is simple and uses pattern matching (another scala great feature!) on the height parameter:
217+
218+
* moving 0 floors does nothing, a no-operation function with no parameter is returned: `() => ()`
219+
* moving one floor calls the elementary move callback, a function with no parameter and calling
220+
`move` is returned: `() => move(from, to)`
221+
* the general case consists in moving one less floor from the source to the spare, moving the
222+
underlying floor to the destination, and repeating the first operation to the destination
223+
224+
### Execution ###
225+
226+
{% highlight scala %}
227+
def play {
228+
229+
println("Building initial city...")
230+
231+
val tower1 = Tower.complete(9)
232+
val tower2 = Tower.foundation
233+
val tower3 = Tower.foundation
234+
235+
val city = City(tower1, tower2, tower3)
236+
237+
println(city)
238+
239+
var success = false
240+
var moves = 0
241+
println("Crane is on the move...")
242+
val go = Move.superMove(city.towers(0).height, 0, 2,
243+
(from: Int, to: Int) => {
244+
Crane move(city, from, to)
245+
println(city)
246+
moves += 1
247+
if(city.towers(0).height == 0 && city.towers(1).height == 0) {
248+
success = true
249+
}
250+
})
251+
252+
go()
253+
254+
println(
255+
if(success)
256+
"Clap clap clap! You are a certified Scala pig! :-) (" + moves + ")"
257+
else
258+
"Pffffff :-(( (" + moves + ")"
259+
)
260+
261+
}
262+
263+
play
264+
{% endhighlight %}
265+
266+
* instantiation via the companion doesn't require the `new` keyword (towers)
267+
* `apply` factory is implicit (city)
268+
* the implementation of the elementary move calls the `Crane` object, prints the new state, counts
269+
the moves and checks for success
270+
271+
Conclusion
272+
----------
273+
274+
Phew, we made it!
275+
276+
The functional part was not so easy to write for the compiler messages are not always obvious.
277+
Especially when the function return types are infered the error is often deported to the caller
278+
code. I guess I will get used to identify the real problems after a few weeks of training.
279+
280+
When I started java I remember my first feeling: waouh, the compiler is so slow! That was biased by
281+
the time it takes to start a JVM. With scala I have the exact same impression. I don't know whether
282+
scala generates java byte code directly (I guess so) or asks the java compiler to compile some java
283+
source code but this phase is CPU consuming. Anyway this doesn't apply in a non scripted enviroment.
284+
285+
The game also showed that I need to learn more about scala collections (mutable or immutable, etc.)
286+
because it is not as obvious as in java. So that is one of my next steps forward along with reading
287+
[Scala By Example](http://www.scala-lang.org/docu/files/ScalaByExample.pdf)
288+
289+
Links
290+
-----
291+
292+
* [The Scala Programming Language](http://www.scala-lang.org/)
293+
* [Getting Started](http://www.scala-lang.org/documentation/getting-started.html)
294+
* [A Scala Tutorial for Java
295+
Programmers](http://docs.scala-lang.org/tutorials/scala-for-java-programmers.html)
296+
* [hanoi-1.scala on GitHub](https://github.com/arnodb/arnodb.learns.scala/blob/master/sandbox/hanoi-1.scala)
297+
* [Scala By Example](http://www.scala-lang.org/docu/files/ScalaByExample.pdf)
298+
299+

_sass/_arnodb.scss

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
.arnodb-center-image {
3+
margin: 0 auto;
4+
display: block;
5+
}
6+
12.7 KB
Loading

css/main.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@ $on-laptop: 800px;
4949
@import
5050
"base",
5151
"layout",
52-
"syntax-highlighting"
52+
"syntax-highlighting",
53+
"arnodb"
5354
;

0 commit comments

Comments
 (0)