├── .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 | ![alt text](http://mitpress.mit.edu/sites/default/files/imagecache/booklist_node/9780262562140.jpg "The Book") 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 | --------------------------------------------------------------------------------