> Seems straightforward enough. My difficulty though comes in trying
> to figure out how to write the winboard bit. I know how to do the
> IO stuff, that's pretty trivial. But, let's say I'm ready to ask
> the engine what move to make in a particular position. The engine
> itself should provide a function that takes a position and returns
> a move. But...and this is where my old OO mindset is probably
> kicking in...there's no way to do something like engine.getMove
> (position), and it does have "its own" functions.
>
> There is only one way I can think of: engine is itself a function.
> When run, it returns a map. One key in the map is, e.g., :get-move.
> The value at that key is the desired function. But...this seems
> rather hackish. I'm sure there's some obvious clojure-ish/lisp-ish
> way of doing this, and it's just not coming to me. Any suggestions?
What else do you want your engine to do? If its only task is to
return a move for a given position, then just make it a function of
position:
(defn engine [position] ...)
Konrad.
Seems straightforward enough. My difficulty though comes in trying to figure out how to write the winboard bit. I know how to do the IO stuff, that's pretty trivial. But, let's say I'm ready to ask the engine what move to make in a particular position. The engine itself should provide a function that takes a position and returns a move. But...and this is where my old OO mindset is probably kicking in...there's no way to do something like engine.getMove(position), and it does have "its own" functions.
Am 26.05.2009 um 14:15 schrieb Rich Hickey:
> Yes - keep your functions out of your data. If you are used to
> engine.getMove(position) it becomes:
>
> (get-move engine position)
>
> If you want polymorphism you can make get-move a multimethod. If the
> 'engine' concept is only about code (e.g. has no data of its own, just
> specifies a protocol), you can simply use names for engines in your
> multimethods:
>
> (get-move :minmax-engine position)
> (get-move :greedy-engine position)
as Rich said: multimethods to the rescue.
You could define a namespace giving the interface of an engine. This
consists of the method definitions and maybe some default
implementations.
(ns car.engine)
(defmulti start
"Start the engine."
type)
(defmulti refuel
"Refuel the tank of the engine."
(fn refuel-dispatch [e _] (type e)))
(defmethod refuel :default
[e amount]
(swap! (:tank e) + amount))
(defmulti explode
"Explode the engine."
type)
(defmethod explode :default
[e]
"BOOOM!")
This basically looks similar to an abstract class in Java. Now we have
to provide some concrete implementation.
(ns car.engine.otto
(:require
[car.engine :as engine]))
(let [type-info {:type ::Otto}]
(defn make-engine
[initial-fuel]
(with-meta {:tank (atom initial-fuel)} type-info)))
(defmethod engine/start ::Otto
[e]
"VROOOOM!")
And another one:
(ns car.engine.diesel
(:require
[car.engine :as engine]))
(let [type-info {:type ::Diesel}]
(defn make-engine
[initial-fuel]
(with-meta {:tank (atom initial-fuel)} type-info)))
(defmethod engine/start ::Diesel
[e]
"TACKTACKTACKTACK!")
(defmethod engine/explode ::Diesel
[e]
"POOOOOOOF!")
Now we use our different implementations.
(ns car.sportscar
(:require
[car.engine :as engine]
[car.engine.otto :as otto]))
(defn make-sportscar
[]
{:engine (otto/make-engine)})
(defn do-drive
[car]
(engine/start (:engine car))
(accelerate car)
(brake car)
(engine/refuel (:engine car) 100)
...)
Or the diesel one....
(ns car.truck
(:require
[car.engine :as engine]
[car.engine.diesel :as diesel]))
(defn make-truck
[]
{:engine (diesel/make-engine)})
....
You get the idea...
Although this went a bit away from the initial chess example,
it should give an idea how the original problem could be solved.
A more real life example is my experimental monad library
based on multimethods (http://bitbucket.org/kotarak/monad).
Hope this helps.
Sincerely
Meikel