A friend was messing
about with the amb
operator in JavaScript after seeing
it in Common Lisp. The amb
(or ambiguous)
operator, first described by our pal John McCarthy (1967), and something
I first encountered
in SICP.
These kind of constraint/logic puzzles seem naturally solved by the list monad, here’s an example of solving ‘Who owns the fish?’ Similar to the Zebra puzzle.
-- Translation of
-- http://mihai.bazon.net/blog/amb-in-javascript/take-two#wotf
import Control.Monad
import Data.Maybe
import Data.List
whoOwnsTheFish = addHouse []
addHouse houses = do
nat <- other "nat" ["British","Swedish","Danish","Norwegian"
,"German"]
col <- other "col" ["red","green","white","yellow","blue"]
pet <- other "pet" ["dogs","cats","horses","birds","fish"]
bev <- other "bev" ["tea","milk","coffee","beer","water"]
tob <- other "tob" ["pallmall","dunhill","marlboro"
,"winfield","rothmans"]
(nat == "British") `iff` (col == "red")
(nat == "Swedish") `iff` (pet == "dogs")
(nat == "Danish") `iff` (bev == "tea")
(col == "white") `iff`
(thisHouse > 0 && "col" `lookup` (houses!!(thisHouse - 1))
== Just "green")
(col == "green") `iff` (bev == "coffee")
(tob == "pallmall") `iff` (pet == "birds")
(col == "yellow") `iff` (tob == "dunhill")
(thisHouse == 2) `iff` (bev == "milk")
(thisHouse == 0) `iff` (nat == "Norwegian")
(tob == "winfield") `iff` (bev == "beer")
(nat == "German") `iff` (tob == "rothmans")
let h = [("nat",nat),("bev",bev),("tob",tob),("pet",pet)
,("col",col)]
a = houses ++ [h]
if length a == 5
then do neighbors a "tob" "marlboro" "pet" "cats"
neighbors a "pet" "horses" "tob" "dunhill"
neighbors a "nat" "Norwegian" "col" "blue"
neighbors a "tob" "marlboro" "bev" "water"
return a
else addHouse a
where other typ = filter (isNothing . findHouse houses typ)
thisHouse = length houses
findHouse houses typ val =
fmap fst . find ((==Just val) . lookup typ . snd) . zip [0..]
$ houses
neighbors houses typ1 val1 typ2 val2 = guard $ diff == Just 1
where diff = do h1 <- findHouse houses typ1 val1
h2 <- findHouse houses typ2 val2
return $ abs $ h1 - h2
iff x y = guard $ x == y