I feel like I should start with a disclaimer: this post is not advocating building an OO system on top of an FP language. And anyway, the Elixir/Erlang “process” model is arguably a very OO system right out of the box. But this series is about working through the FPOO book, and the exercise that’s up next is to implement a basic OO system on top of an FP language, so that’s what I’m going to do.

First version, without knowledge of class:

1

2

3

4

5

6

defmodule Objects1 do

import Dict

def new_point(x,y),do:[x:x,y:y]

defx(point),do:get(point,:x)

defy(point),do:get(point,:y)

end

(Note: when I wrote this I either didn’t know, or had forgotten, that subscript/square-bracket access was available in Elixir. So you’ll see a lot of
get(point,:x) when I probably could have written
point[:x].)

1

2

3

4

5

6

7

8

defmodule TestObjects1 do

import FpOoElx.Exercises.Objects1

test"constructing a Point"do

p=new_point(3,5)

assert(x(p)==3)

assert(y(p)==5)

end

end

Second version, with knowledge of class and
shift method:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

defmodule TestObjects2 do

useExUnit.Case

import FpOoElx.Exercises.Objects2

test"constructing a Point"do

p=new_point(3,5)

assert(x(p)==3)

assert(y(p)==5)

assert(class_of(p)==:point)

p=shift(p,7,-2)

assert(x(p)==10)

assert(y(p)==3)

end

doctest FpOoElx.Exercises.Objects2

end

1

2

3

4

5

6

7

8

9

defmodule Objects2 do

import Dict

def new_point(x,y),do:[x:x,y:y,__class_symbol__::point]

defx(this),do:get(this,:x)

defy(this),do:get(this,:y)

def class_of(object),do:get(object,:__class_symbol__)

def shift(this,xinc,yinc),do:new_point(x(this)+xinc,y(this)+yinc)

<<objects2>>

end

Exercise 1: Implement add

I think I’ll switch over to doctests instead of separate unit tests.

1

2

3

4

5

6

7

8

9

10

11

@doc"""

## Examples:

iex> p1 = new_point(3, 7)

iex> p2 = new_point(8, -3)

iex> p3 = add(p1, p2)

iex> x(p3)

11

iex> y(p3)

4

"""

def add(p1,p2),do:shift(p1,x(p2),y(p2))

Exercise 2: A “new” operator

If I did this exactly like the Clojure version I’d have to call it like this:

Ruby

1

make(&new_point/1,[3,5])

Blerg. I’ll make a macro instead.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@doc"""

## Examples

iex> p = make(point, [3, 5])

iex> class_of(p)

:point

iex> x(p)

3

iex> y(p)

5

"""

defmacro make(class,args)do

{classname,_,_}=class

constructor=binary_to_atom("new_#{classname}")

quote do

unquote(constructor)(unquote_splicing(args))

end

end

OK, that was kinda cool. Of course, if I were willing to put up with passing the classname as a symbol rather than as a bareword, I wouldn’t need a macro.