Y) :- father_child(X, Y). parent_child(X, Y) :- mother_child(X, Y). sibling(X, Y) :- parent_child(Z, X), parent_child(Z, Y). 3 Brian Hicks for Papers we Love St. Louis, as presented May 2017
not a function • unify over variables, which may be unified to values (like X and sally) • various strategies to get results 4 Brian Hicks for Papers we Love St. Louis, as presented May 2017
buried within that 265- line miniKanren implementation is a small, beautiful, relational programming language seeking to get out. We believe µKanren is that language." 7 Brian Hicks for Papers we Love St. Louis, as presented May 2017
goal to a state." type alias Substitution a = Dict Var (Term a) type alias State a = { subs : Substitution a , next : Var } init = { subs = Dict.empty, next = 0 } 9 Brian Hicks for Papers we Love St. Louis, as presented May 2017
a goal pursued to a given state can either succeed or fail." type alias Goal a = State a -> Stream a 10 Brian Hicks for Papers we Love St. Louis, as presented May 2017
(enlarged) states, which we term a stream." type Stream a = Empty | Mature (State a) (Stream a) 11 Brian Hicks for Papers we Love St. Louis, as presented May 2017
• call/fresh - introduce a new term • disj - takes two goals, either of whom may succeed • conj - takes two goals, both of whom must succeed 13 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Substitution a -> Maybe (Substitution a) unify leftVar rightVar subs = let left = walk leftVar subs right = walk rightVar subs in case (left, right) of -- next slides! 14 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Goal a identical left right = \state -> case unify left right state.subs of Just unified -> singleton { state | subs = unified } Nothing -> zero 19 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Goal a callFresh termToGoal = \state -> termToGoal (LVar state.next) { state | next = state.next + 1 } 20 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Stream a mplus s1 s2 = case s1 of Empty -> s2 Mature state stream -> Mature state (mplus s2 stream) 23 Brian Hicks for Papers we Love St. Louis, as presented May 2017
LVal 1) ] , next = 1 } (Mature { substitutions = Dict.fromList [ (0, LVal 2) ] , next = 1 } Empty) 25 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Stream a bind stream goal = case stream of Empty -> zero Mature state next -> mplus (goal state) (bind next goal) 27 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Immature (() -> Stream a) | Mature (State a) (Stream a) zzz : Goal a -> Goal a zzz goal = \stream -> Immature <| \_ -> goal stream 29 Brian Hicks for Papers we Love St. Louis, as presented May 2017
a -> Stream a mplus s1 s2 = case s1 of Empty -> s2 Immature next -> Immature <| \_ -> mplus s2 (next ()) Mature state stream -> Mature state (mplus s2 stream) 30 Brian Hicks for Papers we Love St. Louis, as presented May 2017
a -> Stream a bind stream goal = case stream of Empty -> zero Immature next -> Immature <| \_ -> bind (next ()) goal Mature state next -> mplus (goal state) (bind next goal) 31 Brian Hicks for Papers we Love St. Louis, as presented May 2017
number fives term = disjoin (identical term (LVal 5)) (fives term) This recursive definition works, but causes a stack overflow. fives has to be evaluated in order to evaluate fives. 32 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Goal number fives term = disjoin (identical term (LVal 5)) (zzz <| fives term) Still overflows because fives has to be evaluated to send to zzz, in which fives has to be evaluated to send to zzz and so on. 33 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Goal Int natStartingWith n term = disjoin (identical term (LVal n)) (lazy <| \_ natStartingWith (n + 1) term) nat = natStartingWith 0 37 Brian Hicks for Papers we Love St. Louis, as presented May 2017
a disjoinAll goals = \state -> case goals of g :: rest -> disjoin (g state) (disjoinAll rest) [] -> zero 40 Brian Hicks for Papers we Love St. Louis, as presented May 2017
a conjoinAll goals = \state -> case goals of g :: rest -> conjoin (g state) (conjoinAll rest) [] -> zero 41 Brian Hicks for Papers we Love St. Louis, as presented May 2017
(Goal a) ) -> Goal a conde = List.map (\( condition, body ) -> condition :: body) >> List.map conjoinAll >> disjoinAll 42 Brian Hicks for Papers we Love St. Louis, as presented May 2017
-> Goal a fresh1 fn = callFresh fn fresh2 : (Term a -> Term a -> Goal a) -> Goal a fresh2 fn = fresh1 (\v1 -> callFresh <| fn v1) fresh3 : (Term a -> Term a -> Term a -> Goal a) -> Goal a fresh3 fn = fresh2 (\v1 v2 -> callFresh <| fn v1 v2) 43 Brian Hicks for Papers we Love St. Louis, as presented May 2017
takeAll = case stream of Empty -> Empty Immature _ -> takeAll <| pull stream Mature state stream -> Mature state (takeAll stream) 45 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Stream a take n = if n == 0 then Empty else case stream of Empty -> Empty Immature _ -> take n <| pull stream Mature state stream -> Mature state (take (n - 1) stream) 46 Brian Hicks for Papers we Love St. Louis, as presented May 2017
Stream a) -> List (Term a) runAll = flip fresh1 init >> takeAll >> toList >> List.map reify run : Int -> (Term a -> Stream a) -> Stream a -> List (Term a) run n = flip fresh1 init >> take n >> toList >> List.map reify 48 Brian Hicks for Papers we Love St. Louis, as presented May 2017
-> conjoin (identical out x) (disjoin (identical x (LVal 3)) (identical x (LVal 4))) -- output: [3, 4] 51 Brian Hicks for Papers we Love St. Louis, as presented May 2017
a b a b a b bind andThen Elm convention disjoin / disjoinAll either / any either/any condition can succeed conjoin / conjoinAll both / all both/all conditions must succeed zzz infinite really only useful for infinite lists 53 Brian Hicks for Papers we Love St. Louis, as presented May 2017
a) (Term a) type alias State a = Result (Error a) { substitutions : Substitution a , nextVar : Var } 54 Brian Hicks for Papers we Love St. Louis, as presented May 2017
) -> Term a -> Term a -> Goal a relation tuples left right = tuples |> List.map (\( a, b ) -> both (identical (LVal a) left) (identical (LVal b) right) ) |> any 57 Brian Hicks for Papers we Love St. Louis, as presented May 2017
String -> Goal String parentChild parent child = either (motherChild parent child) (fatherChild parent child) sibling : Term String -> Term String -> Goal String sibling x y = fresh1 <| \parent -> both (parentChild parent x) (parentChild parent y) 59 Brian Hicks for Papers we Love St. Louis, as presented May 2017