/Fing/Unify.fs

http://github.com/sandersn/fing · F# · 162 lines · 123 code · 9 blank · 30 comment · 12 complexity · 8c8b6da64910edb2dfd5706bae437f5b MD5 · raw file

  1. // Copyright (c) 2010, Nathan Sanders
  2. // Licence: New BSD. See accompanying documentation.
  3. module Unify
  4. open Types
  5. open Parser
  6. open Microsoft.FSharp.Metadata
  7. // fancy unification stuff (not implemented yet)
  8. // mainly: list<'a> |=> seq<'a> (interface)
  9. // list<'a> |=> 'a (most-general unification)
  10. // Stack<'a> |=> StretchList<'a> (subtype, I htink my example is wrong)
  11. // (also, with proper respect for co-variance and contra-variance)
  12. // (ugh)
  13. let check t = function // TODO: this is a no-op right now because I can't verify any of these
  14. | Null var -> true // because Id should contain FSharpType or whatever instead of string
  15. | Struct var -> true
  16. | NotStruct var -> true
  17. | DefaultConstructor var -> true
  18. | Enum(var,t) -> true
  19. | Delegate(var,domain,range) -> true
  20. | Subtype(var,t) -> true
  21. | Sig(var,id,t,prop) -> true
  22. | TyparConstraint t -> true
  23. let rec subst' v t = function
  24. | Complex(tag,l) -> Complex(tag, l |> List.map (subst' v t))
  25. | Param i when i = v -> t
  26. | Param j -> Param j
  27. (* substitute type t for variable v in the target type *)
  28. let rec subst v t = function
  29. | Var (Choice is) when is |> List.exists ((=) v) -> t
  30. | Var i when i = v -> t
  31. | Arrow types -> Arrow (List.map (subst v t) types)
  32. | Tuple types -> Tuple (List.map (subst v t) types)
  33. | NamedArg (name,typ,opt) -> NamedArg(name,subst v t typ,opt)
  34. | Generic (typ,types) -> Generic (subst v t typ,List.map (subst v t) types)
  35. | Array (n,typ) -> Array (n,subst v t typ)
  36. | Constraint (quand,typ) when check t quand -> subst v t typ
  37. | typ -> typ // Id and non-matching Var
  38. (* get type variables used in a type *)
  39. let rec stringify = function // this function is cheesy because it's a hack and needs to disappear
  40. | Anonymous -> Set.singleton "_"
  41. | Structural a -> Set.singleton a
  42. | Normal a -> Set.singleton a
  43. | Choice vars -> Set.unionMany (List.map stringify vars)
  44. let rec usedVars = function // should be Typ -> Set<Typar> not Set<string>
  45. | Var v -> Set.singleton v
  46. | Arrow types -> Set.unionMany (List.map usedVars types)
  47. | Tuple types -> Set.unionMany (List.map usedVars types)
  48. | Id _ -> Set.empty
  49. | NamedArg(_,t,_) -> usedVars t
  50. | Generic(t,types) -> usedVars t |>Set.union<| Set.unionMany (List.map usedVars types)
  51. | Array(_,t) -> usedVars t
  52. | Constraint(var,t) -> usedConstraintVars var |>Set.union<| usedVars t
  53. and usedConstraintVars = function
  54. | Null var -> Set.singleton var
  55. | Struct var -> Set.singleton var
  56. | NotStruct var -> Set.singleton var
  57. | DefaultConstructor var -> Set.singleton var
  58. | Enum(var,t) -> var |>Set.add<| usedVars t
  59. | Delegate(var,t,t') -> var |>Set.add<| usedVars t |>Set.union<| usedVars t'
  60. | Subtype(var,t) -> var |>Set.add<| usedVars t
  61. | Sig(var,t,t',_) -> var |>Set.add<| usedVars t |>Set.union<| usedVars t'
  62. | TyparConstraint cons -> Set.unionMany (List.map usedConstraintVars cons)
  63. let rec usedVars' = function
  64. | Param i -> Set.singleton i;
  65. | Complex(tag, l) -> Set.unionMany (List.map usedVars' l)
  66. type Env = list<Typar * Typ>
  67. let both f (x,y) = (f x, f y)
  68. let guard b f = if b then f () else None
  69. (* Find most general unifier (if any) for two types
  70. for example, make sure that
  71. list<'a> -> int <=> list<list<'a>> -> int
  72. 't <=> ('a -> 'b)
  73. 't <=> ('a * 'b)
  74. but
  75. not ('t `mgu`
  76. *)
  77. let (<=>) t1 t2 =
  78. let rec mgu subs = function
  79. | [] -> Some subs
  80. | (Complex(tag1,l1),Complex(tag2,l2))::rest ->
  81. if tag1 <> tag2 then
  82. None
  83. else
  84. let rec loop r = function
  85. | [],[] -> mgu subs r
  86. | [],_ | _,[] -> None
  87. | x::xs, y::ys -> loop ((x,y)::r) (xs,ys)
  88. loop rest (l1,l2)
  89. | (Param i, Param j)::rest when i = j -> mgu subs rest
  90. | ((Param i, x) | (x, Param i))::rest ->
  91. if (Set.contains i (usedVars' x)) then
  92. None (* type would be infinite when unifying *)
  93. else
  94. mgu ((i,x)::subs) (rest |> List.map (fun (t1,t2) -> (subst' i x t1, subst' i x t2)))
  95. let rec mgu' (subs : Env) rest = function
  96. // Note: subs is unneeded here because tyvar substitutions happen immediately
  97. // MAYBE you only need subs with backtracking
  98. // or MAYBE it would be more elegant to use it to look up substitutions each time
  99. | (Arrow l1, Arrow l2) ->
  100. // this version is less efficient than the previous version because it fully traverses
  101. // both lists twice. But it's a lot easier to understand.
  102. guard (List.length l1 = List.length l2)
  103. (fun _ -> Some ([], List.zip l1 l2 @ rest))
  104. | (Tuple l1, Tuple l2) ->
  105. guard (List.length l1 <> List.length l2)
  106. (fun _ -> Some ([], List.zip l1 l2 @ rest))
  107. | (Var (Normal i), Var (Normal j)) when i = j -> Some([],rest)
  108. | (Var (Structural i), Var (Structural j)) when i = j -> Some([],rest)
  109. | ((Var Anonymous, Var _) | (Var _, Var Anonymous)) -> Some([],rest)
  110. | ((Var i, t) | (t, (Var i))) ->
  111. guard (usedVars t |>(Set.contains<| i)) // Infinite type if unified
  112. (fun _ -> Some([(i,t)], (rest |> List.map (both (subst i t)))))
  113. | (Id i, Id j) when i = j -> Some([],rest)
  114. // optional args are treated same as others for now
  115. | (NamedArg (i,t,opt), NamedArg (j,t',opt')) -> Some([],(t,t')::rest)
  116. | (Generic(t,args),Generic(t',args')) when List.length args = List.length args' ->
  117. Some([], (t,t')::List.zip args args')
  118. | (Array(n,t),Array(n',t')) when n = n' -> Some([],(t,t')::rest)
  119. // still have to handle (w,w') somehow, probably need another function for that
  120. | (Constraint(w,t),Constraint(w',t')) -> Some([],(t,t')::rest)
  121. | _ -> None // avoid blue lines for now...
  122. let rec mgu'' (subs : Env) = function
  123. | [] -> Some subs
  124. | (t::ts) ->
  125. match mgu' subs ts t with
  126. | None -> None
  127. | Some(subs',ts') -> mgu'' (subs' @ subs) ts'
  128. // TODO: Finish this!
  129. mgu'' [] [(t1,t2)]
  130. (* finds entities in the F# library with the requested signature, modulo type parameter unification *)
  131. let find' s =
  132. let ty = parse s
  133. let vars = usedVars ty
  134. seq {
  135. for e in FSharpAssembly.FSharpLibrary.Entities do
  136. for m in e.MembersOrValues do
  137. (* need try/catch to avoid error on weird types like "[]`1" *)
  138. match (try Some(FSharpTypes.cvt m.Type) with _ -> None) with
  139. | Some ty2 ->
  140. (* rename all type variables from the query to avoid incorrectly unifying with type variables in signatures *)
  141. let used = usedVars ty2
  142. let newVars = Seq.choose
  143. (fun v ->
  144. if Set.contains (Normal v) used || Set.contains (Structural v) used
  145. then Some(Var (Normal v))
  146. else None)
  147. Types.names
  148. //let ty = Map.fold (fun t v p -> subst v p t) ty varMap
  149. let ty = Map.foldBack subst (Map.ofSeq (Seq.zip vars newVars)) ty
  150. match ty <=> ty2 with
  151. | None -> ()
  152. | Some _ -> yield sprintf "%s.%s.%s" e.Namespace e.DisplayName m.DisplayName
  153. | _ -> () }