{"id":22105117,"url":"https://github.com/strykerkkd/logical","last_synced_at":"2025-07-25T02:31:47.299Z","repository":{"id":77805263,"uuid":"168933858","full_name":"StrykerKKD/Logical","owner":"StrykerKKD","description":"Minimalistic logic programming framework","archived":false,"fork":false,"pushed_at":"2020-02-09T20:28:04.000Z","size":161,"stargazers_count":23,"open_issues_count":5,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-01T21:15:02.400Z","etag":null,"topics":["logic-programming","microkanren","ocaml"],"latest_commit_sha":null,"homepage":"https://strykerkkd.github.io/Logical/","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StrykerKKD.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-03T10:33:48.000Z","updated_at":"2024-07-15T18:00:17.000Z","dependencies_parsed_at":"2023-06-04T15:15:22.916Z","dependency_job_id":null,"html_url":"https://github.com/StrykerKKD/Logical","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/StrykerKKD/Logical","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StrykerKKD%2FLogical","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StrykerKKD%2FLogical/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StrykerKKD%2FLogical/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StrykerKKD%2FLogical/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StrykerKKD","download_url":"https://codeload.github.com/StrykerKKD/Logical/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StrykerKKD%2FLogical/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266944273,"owners_count":24010485,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-25T02:00:09.625Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["logic-programming","microkanren","ocaml"],"created_at":"2024-12-01T06:38:25.856Z","updated_at":"2025-07-25T02:31:47.283Z","avatar_url":"https://github.com/StrykerKKD.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Logical\n\nLogical is a minimalistic logic programming inspired by [microKanren](http://minikanren.org/), which is\n- Simple implementation with only a few building blocks\n- Easy to understand and use\n- Supports negation free constraint logic programming\n\n## How does it work?\nTo understand Logical first you need to understands it's basic building blocks which are the following:\n- Type\n- Value\n- State\n- Goal\n\n### Type\nLogical is basically could be seen as a embedded programming language(or DSL), which allows us to use logic programming in Ocaml. It also means that it has it own type system:\n```ocaml\ntype variable_name = string\n\ntype t = \n    | Int of int\n    | Float of float\n    | Str of string\n    | Bool of bool\n    | Var of variable_name\n```\nYou can see from the type declaration Logical has all the base types as Ocaml(`Int`, `Float`, `Str`, `Bool`), but also has `Var` for variable declarations.\n\n### Value\nThe value module is responsible for making it easier to create value's based on Logical's type system and has the following constructor functions:\n```ocaml\nval int value : int -\u003e int Type.t\n\nval float value : float -\u003e float Type.t\n\nval str value = : string -\u003e string Type.t\n\nval bool value = : bool -\u003e bool Type.t\n\nval var variable_name : variable_name -\u003e variable_name Type.t\n```\n\n### State\nState represents the current state of your program in Logical. State is a map, which stores the value of every currently used variable by using a `variable name -\u003e variable value` association.\nIt's kind of the same as stack frames in other languages.\n\nState's signature:\n```ocaml\ntype state = (variable_name, value) Base.Map.t\n```\nYou can clearly see this in state's type declaration, where `variable_name` is the key of the Map and the `value` is the associated value. You can think of state as a list of variable assignments.\n\nState module's most relevant function are the following:\n```ocaml \n(* represents an empty state *)\nval empty : state \n\n(* creates a new state based on the input association list, which gives back None for duplicated variable names *)\nval create : (variable_name * t) list -\u003e state option\n\n(* creates a new state based on the input association list, which gives back an exception for duplicated variable names *)\nval create_exn : (variable_name * t) list -\u003e state\n\n(* gives back the requested logical value based on the provided state *)\nval value_of : state -\u003e t -\u003e t\n```\n\n### Goal\nGoal is a function, which takes in a state and generates a stream of new states based on that state. The only twist is that sometimes we end up with invalid state(or deadend state), which is why sometimes we can't produce new state based on the input state. \n\nYou can see this in Goal's signature:\n```ocaml\ntype goal = state -\u003e state option Base.Sequence.t\n```\n\nLogical supports the following goals:\n```ocaml\nval equal : value -\u003e value -\u003e goal (* A = B *)\n\nval either : goal -\u003e goal -\u003e goal (* A or B*)\n\nval either_multi : goal list -\u003e goal (* or [A,B,C,..,Y]*)\n\nval both : goal -\u003e goal -\u003e goal (* A and B *)\n\nval both_multi : goal list -\u003e goal (* and [A,B,C,..,Y]*)\n\nval in_set : value -\u003e value -\u003e goal (* A in (A,B,C,...,Y) *)\n```\nYou can see from the type declarations that there are two kinds of goals:\n- Basic building block, which expects 2 `value` and generates a `goal`. You can think of them as constructors for goals.\n- Goal combinators, which expects 2 `goals` and generates new goals based on that.\n\n## How to use it?\nGeneral rules for using Logical:\n- Goal constructors are the most basic building block of every goal\n- Goals can be made bigger and more complex by combining them with goal combinators\n- Goals can be grouped by creating custom goals\n- Goals are inactive until you give them an initial state\n\n### equal\n```ocaml\nlet equal_goal = Goal.equal (Value.var \"a\") (Value.int 42)\nlet state_list = equal_goal State.empty |\u003e Base.Sequence.to_list\n(* state_list is [ Some[(\"a\",Value.Int 42)] ]*)\n```\nIn this case `state_list` only has one state were `a` is equal with `42`.\n\n### either and either_multi\n```ocaml\nlet a_goal = Goal.equal (Value.var \"a\") (Value.int 42)\nlet b_goal = Goal.equal (Value.var \"b\") (Value.int 21)\nlet either_goal = Goal.either a_goal b_goal\nlet state_list = either_goal State.empty |\u003e Base.Sequence.to_list\n(* state_list is [ Some[(\"a\",Value.Int 42)]; Some[(\"b\",Value.Int 21)] ]*)\n```\nIn this case `state_list` has two states where:\n- `a` is equal with `42`\n- `b` is equal with `21`\n\n`either_multi` is the same as `either` only more general, because it expects a list of goals.\n```ocaml\nlet a_goal = Goal.equal (Value.var \"a\") (Value.int 42)\nlet b_goal = Goal.equal (Value.var \"b\") (Value.int 21)\nlet goal_list = [a_goal; b_goal]\nlet either_multi_goal = Goal.either_multi goal_list\nlet state_list = either_multi_goal State.empty |\u003e Base.Sequence.to_list\n(* state_list is [ Some[(\"a\",Value.Int 42)]; Some[(\"b\",Value.Int 21)] ]*)\n```\n\n### both and both_multi\n```ocaml\nlet a_goal = Goal.equal (Value.var \"a\") (Value.int 42)\nlet b_goal = Goal.equal (Value.var \"b\") (Value.int 21)\nlet both_goal = Goal.both a_goal b_goal\nlet state_list = both_goal State.empty |\u003e Base.Sequence.to_list\n(* state_list is [ Some[(\"b\",Value.Int 21); (\"a\",Value.Int 42)] ]*)\n```\nIn this case `state_list` has a state with two assignments where:\n- `a` is equal with `42`\n- `b` is equal with `21`\n\n`both_multi` is the same as `both` only more general, because it expects a list of goals.\n```ocaml\nlet a_goal = Goal.equal (Value.var \"a\") (Value.int 42)\nlet b_goal = Goal.equal (Value.var \"b\") (Value.int 21)\nlet goal_list = [a_goal; b_goal]\nlet both_multi_goal = Goal.both_multi goal_list\nlet state_list = both_multi_goal State.empty |\u003e Base.Sequence.to_list\n(* state_list is [ Some[(\"b\",Value.Int 21); (\"a\",Value.Int 42)] ]*)\n```\n\n### in_set\nin_set goal is basically a syntactic sugar for an `either_multi` where every goal has the same variable.\n```ocaml\nlet my_set = Base.Set.of_list (module Type) [Value.int 42; Value.int 21]\nlet in_set_goal = Goal.in_set (Value.var \"a\") my_set\nlet state_list = in_set_goal State.empty |\u003e Base.Sequence.to_list\n(* state_list is [ Some[(\"a\",Value.Int 42)]; Some[(\"a\",Value.Int 21)] ]*)\n```\nIn this case `state_list` has two states with the same variable(`a`) with two different values: `42` and `21`\n\n`in_set` goal is useful, when you want negation like `x != 6`, which is basically the same as `x in (-infinity,..,5,7,...,infinity)`. From this example you can also see that in_set is only really useful on small finite domains, where the universal set is small and well defined.\n\n### Custom goals\nGoal is basically just a function, which accepts a state and based on that generate a sequence of states, so in order to create custom goals you only have to follow the type definition of the goal function, which is:\n```ocaml\ntype goal = state -\u003e state option Base.Sequence.t\n```\nFor example:\n```ocaml\nlet my_simple_goal state =\n    (*my_simple_goal's implementation*)\nlet my_complex_goal custom_input state = \n    (*my_complex_goal's implementation based on custom_input*)\n\nlet my_big_goal state = \n    Goal.both_multi [\n        my_simple_goal;\n        my_complex_goal custom_input;\n        (fun state -\u003e my_simple_goal state) (*Lambda goal*)\n        (fun state -\u003e my_simple_goal State.empty) (*Independent lambda goal*)\n    ] state\n```\n\nCustom goals are excellent tools for breaking up bigger complex goals into smaller ones and they are also really great fit for generating or creating goals based on some custom extra input parameters. One thing you should be aware is that you are in control of what state you run your custom goals, which gives you a great deal of flexibility.\n\n**Warning**: using `State.value_of` inside of a custom goal could make it dependent on the order of your other goals.\n\n### Note\nIf you like reading code more than guides than you can find the examples in the `bin` folder in this repository.\n\n## How to run the examples?\nPrerequisite: Install [Esy](https://esy.sh/)\n\nRun it: `esy x \u003cexample name\u003e`\n\nFor instance: `esy x example`\n\n## How to for contributors\nPrerequisite: Install [Esy](https://esy.sh/)\n\nBuild it: `esy`\n\nTest it: `esy test`\n\nRun repl: `esy utop`\n\nGenerate Doc: `esy doc`\n\nUpdate docs folder: `esy update-doc`\n\n## Notable resources\n- [Hello, declarative world](https://codon.com/hello-declarative-world)\n- [miniKanren](http://minikanren.org/)\n- [Solving murder with Prolog](https://xmonader.github.io/prolog/2018/12/21/solving-murder-prolog.html)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrykerkkd%2Flogical","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstrykerkkd%2Flogical","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrykerkkd%2Flogical/lists"}