├── .gitattributes
├── .gitignore
├── README.md
└── src
├── logic.core
├── Examples.fsx
├── logic.core.fsproj
└── logic.fs
└── logic.sln
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | bld/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # Roslyn cache directories
20 | *.ide/
21 |
22 | # MSTest test Results
23 | [Tt]est[Rr]esult*/
24 | [Bb]uild[Ll]og.*
25 |
26 | #NUNIT
27 | *.VisualState.xml
28 | TestResult.xml
29 |
30 | # Build Results of an ATL Project
31 | [Dd]ebugPS/
32 | [Rr]eleasePS/
33 | dlldata.c
34 |
35 | *_i.c
36 | *_p.c
37 | *_i.h
38 | *.ilk
39 | *.meta
40 | *.obj
41 | *.pch
42 | *.pdb
43 | *.pgc
44 | *.pgd
45 | *.rsp
46 | *.sbr
47 | *.tlb
48 | *.tli
49 | *.tlh
50 | *.tmp
51 | *.tmp_proj
52 | *.log
53 | *.vspscc
54 | *.vssscc
55 | .builds
56 | *.pidb
57 | *.svclog
58 | *.scc
59 |
60 | # Chutzpah Test files
61 | _Chutzpah*
62 |
63 | # Visual C++ cache files
64 | ipch/
65 | *.aps
66 | *.ncb
67 | *.opensdf
68 | *.sdf
69 | *.cachefile
70 |
71 | # Visual Studio profiler
72 | *.psess
73 | *.vsp
74 | *.vspx
75 |
76 | # TFS 2012 Local Workspace
77 | $tf/
78 |
79 | # Guidance Automation Toolkit
80 | *.gpState
81 |
82 | # ReSharper is a .NET coding add-in
83 | _ReSharper*/
84 | *.[Rr]e[Ss]harper
85 | *.DotSettings.user
86 |
87 | # JustCode is a .NET coding addin-in
88 | .JustCode
89 |
90 | # TeamCity is a build add-in
91 | _TeamCity*
92 |
93 | # DotCover is a Code Coverage Tool
94 | *.dotCover
95 |
96 | # NCrunch
97 | _NCrunch_*
98 | .*crunch*.local.xml
99 |
100 | # MightyMoose
101 | *.mm.*
102 | AutoTest.Net/
103 |
104 | # Web workbench (sass)
105 | .sass-cache/
106 |
107 | # Installshield output folder
108 | [Ee]xpress/
109 |
110 | # DocProject is a documentation generator add-in
111 | DocProject/buildhelp/
112 | DocProject/Help/*.HxT
113 | DocProject/Help/*.HxC
114 | DocProject/Help/*.hhc
115 | DocProject/Help/*.hhk
116 | DocProject/Help/*.hhp
117 | DocProject/Help/Html2
118 | DocProject/Help/html
119 |
120 | # Click-Once directory
121 | publish/
122 |
123 | # Publish Web Output
124 | *.[Pp]ublish.xml
125 | *.azurePubxml
126 | ## TODO: Comment the next line if you want to checkin your
127 | ## web deploy settings but do note that will include unencrypted
128 | ## passwords
129 | #*.pubxml
130 |
131 | # NuGet Packages Directory
132 | packages/*
133 | ## TODO: If the tool you use requires repositories.config
134 | ## uncomment the next line
135 | #!packages/repositories.config
136 |
137 | # Enable "build/" folder in the NuGet Packages folder since
138 | # NuGet packages use it for MSBuild targets.
139 | # This line needs to be after the ignore of the build folder
140 | # (and the packages folder if the line above has been uncommented)
141 | !packages/build/
142 |
143 | # Windows Azure Build Output
144 | csx/
145 | *.build.csdef
146 |
147 | # Windows Store app package directory
148 | AppPackages/
149 |
150 | # Others
151 | sql/
152 | *.Cache
153 | ClientBin/
154 | [Ss]tyle[Cc]op.*
155 | ~$*
156 | *~
157 | *.dbmdl
158 | *.dbproj.schemaview
159 | *.pfx
160 | *.publishsettings
161 | node_modules/
162 |
163 | # RIA/Silverlight projects
164 | Generated_Code/
165 |
166 | # Backup & report files from converting an old project file
167 | # to a newer Visual Studio version. Backup files are not needed,
168 | # because we have git ;-)
169 | _UpgradeReport_Files/
170 | Backup*/
171 | UpgradeLog*.XML
172 | UpgradeLog*.htm
173 |
174 | # SQL Server files
175 | *.mdf
176 | *.ldf
177 |
178 | # Business Intelligence projects
179 | *.rdl.data
180 | *.bim.layout
181 | *.bim_*.settings
182 |
183 | # Microsoft Fakes
184 | FakesAssemblies/
185 |
186 | # LightSwitch generated files
187 | GeneratedArtifacts/
188 | _Pvt_Extensions/
189 | ModelManifest.xml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # logic
2 | A logic programming library for F# based on [miniKanren] and [μKanren]. It is designed to offer an idiomatic F# programming style and also resemble the scheme version of miniKanren.
3 |
4 | ## Example
5 | The peano function in Scheme-miniKanren
6 | ``` scheme
7 | (define peano
8 | (lambda (n)
9 | (conde
10 | ((== 'z n))
11 | ((fresh (n-)
12 | (== `(s. ,n-) n)
13 | (peano n-))))))
14 |
15 | (run 3 (q) (peano q)) ;; '(z (s. z) (s. (s. z)))
16 | ```
17 | the F# version is pretty similar
18 | ``` fsharp
19 | let rec peano n =
20 | logic {
21 | do! conde [ Str "z" == n;
22 | logic {
23 | let! n' = fresh
24 | do! Pair (Str "s", n') == n
25 | return! peano n'
26 | } ]
27 | }
28 |
29 | run 3 (fun q -> peano q) // [Str "z"; Pair (Str "s",Str "z"); Pair (Str "s",Pair (Str "s",Str "z"))]
30 | ```
31 |
32 | ## The Reasoned Schemer
33 | For the functional programmer who wants to learn to think logically there is no better introduction than [The Reasoned Schemer].
34 |
35 | 
36 |
37 |
38 | [miniKanren]: http://minikanren.org/
39 | [μKanren]: http://webyrd.net/scheme-2013/papers/HemannMuKanren2013.pdf
40 | [The Reasoned Schemer]: http://mitpress.mit.edu/books/reasoned-schemer
41 |
--------------------------------------------------------------------------------
/src/logic.core/Examples.fsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | #load "logic.fs"
4 | open logic.core.Logic
5 |
6 |
7 | // Examples
8 | let example1 q =
9 | logic {
10 | let! y = fresh
11 | do! y == Int 42
12 | do! q == y
13 | }
14 |
15 | run 1 example1
16 |
17 | let example2 q =
18 | logic {
19 | let! x = fresh
20 | let! y = fresh
21 | do! x == Int 42
22 | do! y == Int 24
23 | do! q == x
24 | do! q == y
25 | }
26 |
27 | run 1 example2
28 |
29 | let example3 q =
30 | logic {
31 | let! x = fresh
32 | let! y = fresh
33 | do! x == Int 42
34 | do! y == Int 24
35 | do! conde [q == x;
36 | q == y]
37 | }
38 |
39 | run 2 example3
40 |
41 | let rec fives x =
42 | logic {
43 | do! conde [x == Int 5;
44 | logic { return! fives x }]
45 | }
46 |
47 | run 9 fives
48 |
49 | let rec sixes x =
50 | logic {
51 | do! conde [x == Int 6;
52 | logic { return! sixes x }]
53 | }
54 |
55 | run 9 (fun q -> conde [fives q; sixes q])
56 |
57 | let rec peano n =
58 | logic {
59 | do! conde [ Str "z" == n;
60 | logic {
61 | let! n' = fresh
62 | do! Pair (Str "s", n') == n
63 | return! peano n'
64 | } ]
65 | }
66 |
67 | run 3 (fun q -> peano q)
68 |
69 |
70 | let rec appendo l s out =
71 | logic {
72 | do! conde [ logic { do! l == Empty
73 | do! s == out };
74 | logic {
75 | let! a, d, res = fresh3
76 | do! Pair (a, d) == l
77 | do! Pair (a, res) == out
78 | return! appendo d s res
79 | }]
80 | }
81 |
82 | run 2 (fun x -> appendo x (Pair (Int 3, Empty)) (Pair (Int 1, Pair (Int 2, Pair (Int 3, Empty)))))
83 |
--------------------------------------------------------------------------------
/src/logic.core/logic.core.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | 2.0
8 | 04416f06-f907-4ccb-919e-b76f3199df04
9 | Library
10 | logic.core
11 | logic.core
12 | v4.5
13 | 4.3.1.0
14 | logic.core
15 |
16 |
17 | true
18 | full
19 | false
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | 3
24 | bin\Debug\logic.core.XML
25 |
26 |
27 | pdbonly
28 | true
29 | true
30 | bin\Release\
31 | TRACE
32 | 3
33 | bin\Release\logic.core.XML
34 |
35 |
36 |
37 |
38 | True
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | 11
50 |
51 |
52 |
53 |
54 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets
55 |
56 |
57 |
58 |
59 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets
60 |
61 |
62 |
63 |
64 |
71 |
--------------------------------------------------------------------------------
/src/logic.core/logic.fs:
--------------------------------------------------------------------------------
1 | namespace logic.core
2 | open System
3 | open System.Collections.Generic
4 | open System.Linq
5 |
6 | module Logic =
7 |
8 | // Type defs
9 | type VarName = string
10 | type Term =
11 | | Var of VarName
12 | | Pair of Term * Term
13 | | Int of int
14 | | Str of string
15 | | Empty
16 |
17 |
18 | type Subst = (VarName * Term) list
19 | type State = Subst * int
20 | type Goal<'T> = State -> seq
21 |
22 | // walk : Term -> Subst -> Term
23 | let rec walk term subst =
24 | match term with
25 | | Var var ->
26 | match List.tryFind (fun (var', _) -> var = var') subst with
27 | | Some (_, term') -> walk term' subst
28 | | None -> Var var
29 | | _ -> term
30 |
31 | let extend subst (var, term) = (var, term) :: subst
32 |
33 | // unify : Term -> Term -> Subst -> Subst option
34 | let rec unify term term' subst =
35 | match walk term subst, walk term' subst with
36 | | Var var, Var var' ->
37 | if var = var' then
38 | Some subst
39 | else
40 | Some <| extend subst (var, Var var')
41 | | Var var, _ -> Some <| extend subst (var, term')
42 | | _, Var var -> Some <| extend subst (var, term)
43 | | Pair (leftTerm, rightTerm), Pair (leftTerm', rightTerm') ->
44 | match unify leftTerm leftTerm' subst with
45 | | Some subst' -> unify rightTerm rightTerm' subst'
46 | | None -> None
47 | | Int v, Int v' -> if v = v' then Some subst else None
48 | | Str v, Str v' -> if v = v' then Some subst else None
49 | | Empty, Empty -> Some subst
50 | | _, _ -> None
51 |
52 |
53 | // disj : Goal<'T> -> Goal<'T> -> Goal<'T>
54 | let disj (goal : Goal<'T>) (goal' : Goal<'T>) =
55 | fun (state, counter) ->
56 | let rec interleave (enum : Lazy>) (enum' : Lazy>) =
57 | seq {
58 | let flag = enum.Force().MoveNext()
59 | if flag then
60 | yield enum.Force().Current
61 |
62 | let flag' = enum'.Force().MoveNext()
63 | if flag' then
64 | yield enum'.Force().Current
65 |
66 | if flag || flag' then
67 | yield! interleave enum enum'
68 | }
69 |
70 | let enum = lazy ((goal (state, counter)).GetEnumerator())
71 | let enum' = lazy ((goal' (state, counter)).GetEnumerator())
72 | interleave enum enum'
73 |
74 | // conde : Goal<'T> list -> Goal<'T>
75 | let conde list = List.reduce disj list
76 |
77 | // (==) : Term -> Term -> Goal
78 | let (==) (term : Term) (term' : Term) : Goal =
79 | fun (subst, counter) ->
80 | match unify term term' subst with
81 | | Some subst' -> seq { yield (subst', counter), () }
82 | | None -> Seq.empty
83 |
84 | // fresh : Goal
85 | let fresh : Goal = fun (subst, counter) -> seq { yield ((subst, counter + 1), (Var (sprintf "var%d" counter))) }
86 | // fresh2 : Goal
87 | let fresh2 : Goal = fun (subst, counter) -> seq { yield ((subst, counter + 2), (Var (sprintf "var%d" counter), Var (sprintf "var%d" (counter + 1)))) }
88 | // fresh3 : Goal
89 | let fresh3 : Goal = fun (subst, counter) -> seq { yield ((subst, counter + 3), (Var (sprintf "var%d" counter), Var (sprintf "var%d" (counter + 1)), Var (sprintf "var%d" (counter + 2)))) }
90 |
91 | // substVars : Subst -> Term -> Term
92 | let rec substVars subst term =
93 | match term with
94 | | Var _ ->
95 | let term' = walk term subst
96 | if term = term' then term
97 | else substVars subst term'
98 | | Pair (left, right) -> Pair (substVars subst left, substVars subst right)
99 | | Int _ -> term
100 | | Str _ -> term
101 | | Empty -> term
102 |
103 | // print : string -> Term -> Goal
104 | let print name (term : Term) =
105 | fun (subst, counter) ->
106 | printfn "%s = %A" name (substVars subst (walk term subst))
107 | seq { yield ((subst, counter), ()) }
108 |
109 | // run : int -> (Term -> Goal<'T>) -> Term list
110 | let run n (f : Term -> Goal<'T>) =
111 | let goalVar = Var "__goal__"
112 | let seq =
113 | ([], 0)
114 | |> f goalVar
115 | seq.Take(n)
116 | |> Seq.map (fun ((subst, _), _) -> substVars subst (walk goalVar subst))
117 | |> Seq.toList
118 |
119 |
120 | type LogicBuilder() =
121 |
122 | member this.Zero() = fun _ -> Seq.empty
123 | member this.Return v = fun (subst, counter) -> seq { yield ((subst, counter), v) }
124 | member this.ReturnFrom goal = goal
125 |
126 | member this.Bind (goal : Goal<'T>, f : 'T -> Goal<'R>) : Goal<'R> =
127 | fun (subst, counter) ->
128 | (subst, counter)
129 | |> goal
130 | |> Seq.collect (fun ((subst', counter'), v) ->
131 | f v (subst', counter'))
132 |
133 | member this.Delay (f : unit -> Goal<'T>) : Goal<'T> =
134 | fun (subst, counter) ->
135 | f () (subst, counter)
136 |
137 | let logic = new LogicBuilder()
138 |
139 |
140 |
--------------------------------------------------------------------------------
/src/logic.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "logic.core", "logic.core\logic.core.fsproj", "{04416F06-F907-4CCB-919E-B76F3199DF04}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {04416F06-F907-4CCB-919E-B76F3199DF04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {04416F06-F907-4CCB-919E-B76F3199DF04}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {04416F06-F907-4CCB-919E-B76F3199DF04}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {04416F06-F907-4CCB-919E-B76F3199DF04}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------