Skip to content

Commit e8ea680

Browse files
authored
Merge pull request #152 from engboris/fix
Implement ~= as term unifiability instead of ray compatibility
2 parents 7eb91bd + 683e619 commit e8ea680

File tree

5 files changed

+58
-21
lines changed

5 files changed

+58
-21
lines changed

BASICS.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,15 @@ a $ :b c
7777

7878
**Unification** = finding substitutions that make two terms identical.
7979

80+
You can check if two terms are matchable with the term `(~= t u)` where `t` and
81+
`u` are terms.
82+
8083
For example:
8184

8285
```stellogen
83-
'''
84-
(f X) ~ (f (h a)) => they match with {X := (h a)}
85-
(f X) ~ X => ❌ (circular)
86-
(f X) ~ (g X) => ❌ (they don't match because different head symbol)
87-
'''
86+
(~= (f X) (f (h a))) ' => they match with {X := (h a)}
87+
(~= (f X) X) ' => ❌ (circular)
88+
(~= (f X) (g X)) ' => ❌ (they don't match because different head symbol)
8889
```
8990

9091
A **ray** is a term with polarity:
@@ -94,12 +95,13 @@ A **ray** is a term with polarity:
9495
* `(f X)` → neutral (does not interact)
9596

9697
Two rays and **compatible** and can interact if they have opposite polarities
97-
**and** their terms unify. You can check compatibility with the term
98-
`(~= t u)` with arguments `t` and `u` which are rays.
98+
**and** their terms unify.
9999

100100
```stellogen
101-
(~= (+f X) (-f (h a))) ' => succeeds with {X := (h a)}
102-
' (~= (+f X) (+f a)) ' => ❌ (fails because same polarity)
101+
'''
102+
(+f X) and (-f (h a))) are compatible with {X := (h a)}
103+
(+f X) and (+f a)) are incompatible because they have same head polarity
104+
'''
103105
```
104106

105107
---

examples/proofnets/mll.sg

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,11 @@
115115
'
116116
' [3, 6]
117117
'
118-
' This corresponds to the following executio in stellogen
118+
' This corresponds to the following execution in stellogen
119119
' in which [] is a way to initiate a starting point in computation
120120
(:= comp (exec #x @[(-3 X) (+3 X)]))
121-
(:= res { [(+3 X9) (+6 X9)] })
122-
' (== #comp #res)
121+
(:= res { [(+3 X) (+6 X)] })
122+
(~= #comp #res)
123123

124124
' as for the proof of identity (A ⊗ B) -o (A ⊗ B):
125125
'

src/lsc_ast.ml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,25 @@ module StellarSig = struct
2727
| _ -> false
2828
end
2929

30+
module MatchableSig = struct
31+
type idvar = string * int option
32+
33+
type idfunc = polarity * string
34+
35+
let string_of_idvar (s, index_opt) =
36+
match index_opt with None -> s | Some j -> s ^ Int.to_string j
37+
38+
let equal_idvar x y = String.equal (string_of_idvar x) (string_of_idvar y)
39+
40+
let equal_idfunc (p1, f1) (p2, f2) =
41+
equal_polarity p1 p2 && String.equal f1 f2
42+
43+
(* Matchable: only checks function name equality, ignores polarity *)
44+
let compatible (_p1, f1) (_p2, f2) = String.equal f1 f2
45+
end
46+
3047
module StellarRays = Unification.Make (StellarSig)
48+
module MatchableRays = Unification.Make (MatchableSig)
3149
open StellarRays
3250

3351
(* ---------------------------------------
@@ -98,6 +116,18 @@ let replace_indices (i : int) : ray -> ray =
98116
let raymatcher r r' : substitution option =
99117
if is_polarised r && is_polarised r' then solution [ (r, r') ] else None
100118

119+
(* Convert StellarRays.term to MatchableRays.term *)
120+
let rec to_matchable_term : StellarRays.term -> MatchableRays.term = function
121+
| StellarRays.Var x -> MatchableRays.Var x
122+
| StellarRays.Func (f, ts) ->
123+
MatchableRays.Func (f, List.map ~f:to_matchable_term ts)
124+
125+
(* Check if two rays can unify using term unification (ignoring polarity) *)
126+
let terms_unifiable r r' =
127+
let r_match = to_matchable_term r in
128+
let r'_match = to_matchable_term r' in
129+
MatchableRays.solution [ (r_match, r'_match) ] |> Option.is_some
130+
101131
let fresh_var vars =
102132
let rec find_fresh_index i =
103133
if List.mem vars ("X", Some i) ~equal:StellarSig.equal_idvar then

src/sgen_eval.ml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ let ( let* ) x f = Result.bind x ~f
99

1010
let unifiable r r' = StellarRays.solution [ (r, r') ] |> Option.is_some
1111

12-
let constellations_compatible c1 c2 =
13-
(* Check if two constellations are compatible for Match (~=) *)
14-
(* Uses unifiable which requires opposite polarities for interaction *)
12+
let constellations_matchable c1 c2 =
13+
(* Check if two constellations are matchable for Match (~=) *)
14+
(* Uses term unification (ignoring polarity, only checking function name equality) *)
1515
let open Lsc_ast in
1616
(* Extract all rays from both constellations *)
1717
let rays_from_constellation c =
@@ -20,9 +20,9 @@ let constellations_compatible c1 c2 =
2020
let rays1 = rays_from_constellation c1 in
2121
let rays2 = rays_from_constellation c2 in
2222

23-
(* Check if any ray from c1 is unifiable with any ray from c2 *)
23+
(* Check if any ray from c1 can unify with any ray from c2 (ignoring polarity) *)
2424
List.exists rays1 ~f:(fun r1 ->
25-
List.exists rays2 ~f:(fun r2 -> unifiable r1 r2) )
25+
List.exists rays2 ~f:(fun r2 -> terms_unifiable r1 r2) )
2626

2727
let rec find_with_solution env x =
2828
let rec search_objs = function
@@ -32,7 +32,12 @@ let rec find_with_solution env x =
3232
let value_normalized = map_ray env ~f:(replace_indices 0) value in
3333
let x_normalized = replace_indices 1 x in
3434
match StellarRays.solution [ (key_normalized, x_normalized) ] with
35-
| Some substitution -> Some (value_normalized, substitution)
35+
| Some substitution ->
36+
(* Only use renamed value if there's actual parameter substitution *)
37+
let result_value =
38+
if List.is_empty substitution then value else value_normalized
39+
in
40+
Some (result_value, substitution)
3641
| None -> search_objs rest )
3742
in
3843
search_objs env.objs
@@ -299,7 +304,7 @@ let rec eval_sgen_expr (env : env) :
299304
let* env2, eval2 = eval_sgen_expr env1 expr2 in
300305
let const1 = List.map eval1 ~f:Marked.remove in
301306
let const2 = List.map eval2 ~f:Marked.remove in
302-
if constellations_compatible const1 const2 then Ok (env2, [])
307+
if constellations_matchable const1 const2 then Ok (env2, [])
303308
else Error (MatchError { term1 = eval1; term2 = eval2; message; location })
304309
| Use path -> (
305310
let open Lsc_ast.StellarRays in

test/examples.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ NPDA (non-deterministic pushdown automaton) example:
5555
Prolog-style examples:
5656
$ sgen run ../examples/prolog.sg
5757
(s (s (s (s 0))))
58-
[(-grandparent tom Z0) Z0]
58+
[(-grandparent tom Z) Z]
5959
{ [ok (-to 1)] [(-from 5)] [(-from 4)] [(-from 5)] }
6060

6161
Stack example:
@@ -72,7 +72,7 @@ Syntax reference:
7272
{ [c] [b] [a] }
7373
(%string hello world)
7474
(function a b)
75-
{ [(-f X0) (-f Y0) (r X0 Y0) || (!= X Y)] [(+f b)] [(+f a)] }
75+
{ [(-f X) (-f Y) (r X Y) || (!= X Y)] [(+f b)] [(+f a)] }
7676
{ [(r b a) || (!= b a)] [(r a b) || (!= a b)] }
7777
(+n2 (s (s 0)))
7878
{ [(+field test2) (%cons (+f b) (%cons ok %nil))] [(+field test1) (%cons (+f a) (%cons ok %nil))] }

0 commit comments

Comments
 (0)