package alba
Alba compiler
Install
Dune Dependency
Authors
Maintainers
Sources
0.4.3.tar.gz
sha256=062f33c55ef39706c4290dff67d5a00bf009051fd757f9352be527f629ae21fc
md5=eb4edc4d6b7e15b83d6397bd34994153
doc/src/alba.core/unifier.ml.html
Source file unifier.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
(* Unification ----------- We have a context with holes gh and two welltyped terms t1 and t2. The unification problem is expressed as on of gh |- le! t1 t2 gh |- eq! t1 t2 requiring to fill the holes in gh such that the substituted terms satisfy gh |- t1 <= t2 gh |- t1 = t2 where `=` means beta equality. The unification fails, if no substitution can be found for the holes (or metavariables) in gh. If the terms t1 ant t2 contain no holes, unification is possible only if both expressions satisfy the required relation. This implies that the normal forms both terms have the same structure. Algorithm --------- The algorithm is a recursive procedure which recurses over the normal form of both terms. In each step we transform the two terms u and v into the key normal forms t1 = k1 a0 a1 ... t2 = k2 b0 b1 ... The key normal form is always one of s lambda x:A . e Pi x:A . B x a0 a1 ... i.e. arguments can only be present if the key is a variable. Since we might enter subexpressions with binders, the context might consist of gh, psi where psi represents the entered binders. psi does not contain any holes. The interesting cases are when one of the keys or both keys are holes. In all other cases futhere unification is possible only if both expressions have the same structure. Filling of holes ---------------- Replace with lambda term (application to local variables only): Suppose we have to unify gh,psi |- rel! (f x y ...) t where f is a hole and x y ... are variables in psi (might be a subset). This is possible only if gh |- f: all (x: A) (y: B) ... : R is valid. We can fill the hole with f := \ x y ... := t if the following conditions are satisfied: - If t contains variables of psi, then they all belong to the set x y ... - type (\x y ... := t) <= type f Mirror image `gh,psi |- rel! t (f x y ...)` is practically the same: Both are holes (local variables only): Suppose we have to unify gh,psi |- rel! (f x y ...) (g x y ...) where f and g are holes and x y ... are variables in psi (might be subset). f := g, if gh |- type g <= type f g := f, if gh |- type f <= type g If either f or g is applied to fewer local variables than the other, we have to apply the first case to the hole which is applied to more variables (case is no longer symmetrical). Both are holes (local variables only) with different order of variables Suppose we have to unify gh,psi |- rel! (f x y ...) (g y x ...) where f and g are holes and x y ... are variables in psi (possibly subset). f := \ x y ... := g y x ..., if type (\ x y ...) <= type f g := \ y x ... := f x y ..., if type (\ y x ...) <= type g Replace with variable (general application): Suppose we have to unify gh,psi |- rel! (f a0 a1 ...) (x b0 b1 ...) where f is a hole and x is not a hole. f := x, if type x <= type f and then unify eq! a0 b0, eq! a1 b2. The condition `type x <= type f` implies that the number of arguments on both sides is the same. Substitution Decision --------------------- Situtations: - key is a hole, i.e. on term has the form f x y ... - lambda condition satisfied: {x,y,...} subset |psi| and the other term contains not more than {x,y,...} from psi. Interesting cases: - One key is a hole and satisfies the lambda condition and the other is not a hole or does not satisfy the lambda condition -> do lambda, if possible or fail - Both keys are holes satisfying the lambda condition. Then distinguish - arguments are identical -> substitute subtype for the other or fail if none of the types is a subtype of the other - arguments are different -> do lambda the way it is possible or fail. In all other cases we have to do structural unification. Generalisation -------------- Whenever we have to unify rel! (f a b c ...) t we can try the substitution f := (\ x y ... := t) provided that type (\ x y ... := t) <= type f and t does not contain more local variables than [f a b c ...] and a b c ... are either local variables or do not contain local variables. *) open Fmlib open Common module type HOLES = sig include Gamma_algo.GAMMA val context: t -> Gamma.t val expand: Term.t -> t -> Term.t val is_hole: int -> t -> bool val value: int -> t -> Term.t option val fill_hole0: int -> Term.t -> bool -> t -> t val fold_entries: (int -> int -> string -> Term.typ -> bool -> Term.t option -> 'a -> 'a) -> t -> 'a -> 'a end module Unification_context (Gh: HOLES) = struct type t = { gh: Gh.t; gamma_top: Gamma.t; stack: Gamma.t list; } let base (uc: t): Gh.t = uc.gh let gamma (uc: t): Gamma.t = uc.gamma_top let nlocals (uc: t): int = Gamma.count uc.gamma_top - Gh.count uc.gh let count (uc: t): int = Gamma.count uc.gamma_top let is_valid_index (idx: int) (uc: t): bool = idx < count uc let name_of_index (idx: int) (uc: t): string = assert (is_valid_index idx uc); Gamma.name_of_index idx uc.gamma_top let definition_term (idx: int) (uc: t): Term.t option = assert (is_valid_index idx uc); Gamma.definition_term idx uc.gamma_top let type_of_literal (lit: Term.Value.t) (uc: t): Term.typ = Gamma.type_of_literal lit uc.gamma_top let is_hole (idx: int) (uc: t): bool = let nlocs = nlocals uc in if idx < nlocs then false else Gh.is_hole (idx - nlocs) uc.gh let is_empty_hole (idx: int) (uc: t): bool = let nlocs = nlocals uc in if idx < nlocs then false else Gh.value (idx - nlocs) uc.gh = None let fill_hole (idx: int) (typ: Term.typ) (uc: t) : t option = assert (is_hole idx uc); let nlocs = nlocals uc in Option.map (fun typ0 -> {uc with gh = Gh.fill_hole0 (idx - nlocs) typ0 true uc.gh}) (Term.down nlocs typ) let expand (term: Term.t) (uc: t): Term.t = let nlocs = nlocals uc in Term.substitute_with_beta (fun i -> if i < nlocs then Variable i else match Gh.value (i - nlocs) uc.gh with | None -> Variable i | Some term -> Term.up nlocs term) term let type_of_variable (idx: int) (uc: t): Term.typ = assert (is_valid_index idx uc); let nlocs = nlocals uc in if idx < nlocs then (*expand (Gamma.type_of_variable idx uc.gamma_top) uc*) Gamma.type_of_variable idx uc.gamma_top else Term.up nlocs (Gh.type_of_variable (idx - nlocs) uc.gh) let string_of_term (term: Term.t) (uc: t): string = Term_printer.string_of_term term uc.gamma_top let _ = string_of_term let make (gh: Gh.t): t = { gh; gamma_top = Gh.context gh; stack = [] } let push (name: string) (typ: Term.typ) (uc: t): t = { uc with gamma_top = Gamma.push_local name typ uc.gamma_top; stack = uc.gamma_top :: uc.stack; } let push_local = push let pop (uc: t): t = match uc.stack with | [] -> assert false (* Illegal call! *) | gamma_top :: stack -> {uc with gamma_top; stack} end (* Unification_context *) module Make (Gh: HOLES) = struct module Uc = Unification_context (Gh) module Algo = Gamma_algo.Make (Uc) type path_step = Term.t * Term.t * bool * Gamma.t type path = path_step list type args = (Term.t * Term.Application_info.t) array type 'a result2 = ('a, path * Gh.t) result module Result2 = Result.Make (struct type t = path * Gh.t end) let key_split (term: Term.typ) (uc: Uc.t): Term.t * args = let key, args = Algo.key_split (Uc.expand term uc) uc in key, Array.of_list args let unify_sorts (act_sort: Term.Sort.t) (req_sort: Term.Sort.t) (is_super: bool) (path: path) (uc: Uc.t): Uc.t result2 = if (is_super && Term.Sort.is_super req_sort act_sort) || (not is_super && req_sort = act_sort) then Ok uc else Error (path, Uc.base uc) let make_lambda (idx_hole: int) (args: args) (term: Term.t) (path: path) (uc: Uc.t): Term.t result2 = let nlocs = Uc.nlocals uc and nargs = Array.length args and error _ = Error (path, Uc.base uc) and idx_map = ref Int_map.empty in let module TMon = Term.Monadic (Result2) in let transform iarg = TMon.map_free (fun i -> if i = idx_hole then error () else match Int_map.maybe_find i !idx_map with | None -> if i < nlocs then error () else Ok (i + iarg) | Some i_new -> Ok i_new ) in let rec make iarg = let open Term in if iarg = nargs then transform nargs term else let open Result in match args.(iarg) with | Variable i, _ when not (Int_map.mem i !idx_map) -> idx_map := Int_map.add i (bruijn_convert iarg nargs) !idx_map; make (iarg + 1) >>= fun exp -> transform iarg (Uc.type_of_variable i uc) >>= fun arg_typ -> Ok (lambda (Uc.name_of_index i uc) arg_typ exp) | arg, _ -> make (iarg + 1) >>= fun exp -> transform iarg (Algo.type_of_term arg uc) >>= fun arg_typ -> Ok (lambda "_" arg_typ exp) in make 0 let rec unify0 (act: Term.t) (req: Term.t) (is_super: bool) (path: path) (uc: Uc.t): Uc.t result2 = let act_key, act_args = key_split act uc and path = (act, req, is_super, (Uc.gamma uc)) :: path in let open Term in match act_key with | Typed _ | Appl _ | Where (_, _, _, _) -> assert false (* Cannot happen in key splitted form. *) | Sort act_sort -> assert (Array.is_empty act_args); let req_key, req_args = key_split req uc in begin match req_key with | Typed _ | Appl _ | Where (_, _, _, _) -> assert false (* Cannot happen in key splitted form. *) | Value _ | Lambda _ | Pi _ -> Error (path, Uc.base uc) | Variable j when not (Uc.is_hole j uc) -> Error (path, Uc.base uc) | Sort req_sort -> assert (Array.is_empty req_args); unify_sorts act_sort req_sort is_super path uc | Variable j -> assert (Uc.is_hole j uc); fill_hole j req_args act path uc end | Value act_value -> assert (Array.is_empty act_args); let req_key, req_args = key_split req uc in ( match req_key with | Typed _ | Appl _ | Where (_, _, _, _) -> assert false (* Cannot happen in key splitted form. *) | Value req_value when act_value = req_value -> assert (Array.is_empty req_args); Ok uc | Variable j when Uc.is_hole j uc -> assert (Uc.is_empty_hole j uc); fill_hole j req_args act path uc | _ -> Error (path, Uc.base uc) ) | Variable i -> let req_key, req_args = key_split req uc in ( match req_key with | Typed _ | Appl _ | Where (_, _, _, _) -> assert false (* Cannot happen in key splitted form. *) | Variable j -> unify_variable_variable i act_args act j req_args req path uc | _ -> if Uc.is_hole i uc then fill_hole i act_args req path uc else Error (path, Uc.base uc) ) | Lambda (_, _, _) -> assert (Array.is_empty act_args); assert false (* nyi *) | Pi (act_arg, act_res, info) -> assert (Array.is_empty act_args); ( let req_key, req_args = key_split req uc in match req_key with | Variable j when Uc.is_hole j uc -> assert (Uc.is_empty_hole j uc); fill_hole j req_args act path uc | Pi (req_arg, req_res, _) -> let open Result in unify0 act_arg req_arg false path uc >>= unify_pushed (Pi_info.name info) act_arg act_res req_res true path | _ -> Error (path, Uc.base uc) ) and unify_variable_variable (i: int) (iargs: args) (iterm: Term.t) (j: int) (jargs: args) (jterm: Term.t) (path: path) (uc: Uc.t): Uc.t result2 = if i = j then begin let nargs = Array.length iargs in assert (nargs = Array.length jargs); let module IntMon = Interval.Monadic (Result2) in IntMon.fold (fun k -> unify0 (fst iargs.(k)) (fst jargs.(k)) false path ) 0 nargs uc end else let ihole = Uc.is_hole i uc and jhole = Uc.is_hole j uc in if ihole && jhole then Result2.catch (fill_hole i iargs jterm path uc) (fun _ -> fill_hole j jargs iterm path uc) else if ihole then fill_hole i iargs jterm path uc else if jhole then fill_hole j jargs iterm path uc else Error (path, Uc.base uc) and unify_pushed (name: string) (typ: Term.typ) (act: Term.t) (req: Term.t) (is_super: bool) (path: path) (uc: Uc.t): Uc.t result2 = Result.map Uc.pop (unify0 act req is_super path (Uc.push name typ uc)) and fill_hole (idx: int) (args: args) (term: Term.t) (path: path) (uc: Uc.t): Uc.t result2 = (* unify (idx args) term, where [idx] is a hole and [term] is not starting with a hole. The lambda condition has not yet been checked. *) assert (Uc.is_empty_hole idx uc); let open Result2 in make_lambda idx args term path uc >>= fun lambda -> fill_simple_hole idx lambda path uc and fill_simple_hole (idx: int) (term: Term.t) (path: path) (uc: Uc.t): Uc.t result2 = assert (Uc.is_empty_hole idx uc); let typ_hole = Uc.type_of_variable idx uc and typ_term = Algo.type_of_term term uc in Result.( unify0 typ_term typ_hole true path uc >>= fun uc -> match Uc.fill_hole idx term uc with | None -> Printf.printf " shall never happen\n"; Error (path, Uc.base uc) | Some uc -> Ok uc ) let print_holes (gh: Gh.t): unit = let string_of term = Term_printer.string_of_term term (Gh.context gh) in Gh.fold_entries (fun _ _ name typ is_hole value () -> let open Printf in if is_hole then match value with | None -> printf " %s: %s\n" name (string_of typ) | Some value -> printf " %s: %s := %s\n" name (string_of typ) (string_of value) else printf " %s: %s\n" name (string_of typ) ) gh () let _ = print_holes let unify (act: Term.typ) (req: Term.typ) (is_super: bool) (gh: Gh.t): Gh.t option = match unify0 act req is_super [] (Uc.make gh) with | Ok uc -> (*let open Printf in let open Term_printer in let gamma = Gh.context gh in printf "\nsuccessful unification %s with %s\n" (string_of_term act gamma) (string_of_term req gamma); printf "holes\n"; print_holes gh; printf "\n";*) Some (Uc.base uc) | Error (_, _) -> (*let open Printf in let print ((act,req,is_super,gamma)) = let open Term_printer in printf " %s with %s (%b)\n" (string_of_term act gamma) (string_of_term req gamma) is_super in printf "\nfailed unification\n"; List.iter print path; printf "holes\n"; print_holes gh; printf "\n";*) None end (* Make *)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>