my blog lives here now
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

126 lines
5.3 KiB

6 years ago
  1. ---
  2. title: "A Quickie: A Use Case for Impredicative Polymorphism"
  3. path: impredicative-polymorphism
  4. date: October 19, 2019
  5. ---
  6. Amulet now (as of the 18th of October) has support for impredicative
  7. polymorphism based on [Quick Look impredicativity], an algorithm first
  8. proposed for GHC that treats inference of applications as a two-step
  9. process to enable inferring impredicative types.
  10. As a refresher, impredicative types (in Amulet) are types in which a
  11. `forall`{.amuçet} appears under a type constructor (that is not
  12. `(->)`{.amulet} or `(*)`{.amulet}, since those have special variance in
  13. the compiler).
  14. Quick Look impredicativity works by doing type checking of applications
  15. in two phases: the _quick look_, which is called so because it's faster
  16. than regular type inference, and the regular type-checking of
  17. arguments.
  18. Given a `n`-ary application
  19. <code>f x<sub>1</sub> ... x<sub>n</sub></code>:
  20. <ol type="I">
  21. <li>
  22. The _quick look_ proceeds by inferring the type of the function to
  23. expose the first `n` quantifiers, based on the form of the arguments.
  24. For a regular term argument `e`, we expect a `'t ->`{.amulet} quantifier; For
  25. visible type arguments, we expect either `forall 'a.`{.amulet} or
  26. `forall 'a ->`{.amulet}.
  27. After we have each of the quantifiers, we quickly infer a type for each
  28. of the _simple_ arguments in
  29. <code>x<sub>1</sub> ... x<sub>n</sub></code>.
  30. Here, simple means either a
  31. variable, literal, application or an expression annotated with a type
  32. `x : t`{.amulet}. With this type in hands, we unify it with the type
  33. expected by the quantifier, to collect a partial substituion (in which
  34. unification failures are ignored), used to discover impredicative
  35. instantiation.
  36. For example, say `f : 'a -> list 'a -> list 'a`{.amulet} (the cons
  37. function)[^1], and we want to infer the application `f (fun x -> x) (Nil
  38. @(forall 'xx. 'xx -> 'xx))`{.amulet}. Here, the quick look will inspect
  39. each argument in turn, coming up with a `list 'a ~ list (forall 'xx. 'xx
  40. -> 'xx)`{.amulet} equality by looking at the second argument. Since the
  41. first argument is not simple, it tells us nothing. Thus, the second
  42. phase starts with the substitution `'a := forall 'xx. 'xx ->
  43. 'xx`{.amulet}.
  44. </li>
  45. <li>
  46. The second phase is traditional type-checking of each argument in turn,
  47. against its respective quantifier. Here we use Amulet's type-checking
  48. function `check` instead of applying type-inference then constraining
  49. with subsumption since that results in more precise resuls.
  50. However, instead of taking the quantifiers directly from the function's
  51. inferred type, we first apply the substitution generated by the
  52. quick-look. Thus, keeping with the example, we check the function `(fun
  53. x -> x)`{.amulet} against the type `forall 'xx. 'xx -> 'xx`{.amulet},
  54. instead of checking it against the type variable `'a`{.amulet}.
  55. This is important because checking against a type variable degrades to
  56. inference + subsumption, which we wanted to avoid in the first place!
  57. Thus, if we had no quick look, the function `(fun x -> x)`{.amulet}
  58. would be given monomorphic type `'t1 -> 't2`{.amulet} (where
  59. `'t1'`{.amulet}, `'t2`{.amulet} are fresh unification variables), and
  60. we'd try to unify `list ('t1 -> 't2) ~ list (forall 'xx. 'xx ->
  61. 'xx)`{.amulet} - No dice!
  62. </li>
  63. </ol>
  64. ### Why does this matter?
  65. Most papers discussing impredicative polymorphism focus on the boring,
  66. useless example of stuffing a list with identity functions. Indeed, this
  67. is what I demonstrated above.
  68. However, a much more useful example is putting _lenses_ in lists (or
  69. `optional`{.amulet}, `either`{.amulet}, or what have you). Recall the
  70. van Laarhoven encoding of lenses:
  71. ```amulet
  72. type lens 's 't 'a 'b <- forall 'f. functor 'f => ('a -> 'f 'b) -> 's -> 'f 't
  73. ```
  74. If you're not a fan of that, consider also the profunctor encoding of
  75. lenses:
  76. ```amulet
  77. type lens 's 't 'a 'b <- forall 'p. strong 'p => 'p 'a 'b -> 'p 's 't
  78. ```
  79. These types are _both_ polymorphic, which means we can't normally have a
  80. `list (lens _ _ _ _)`{.amulet}. This is an issue! The Haskell `lens`
  81. library works around this by providing a `LensLike`{.haskell} type,
  82. which is not polymorphic and takes the functor `f` by means of an
  83. additional parameter. However, consider the difference in denotation
  84. between
  85. ```haskell
  86. foo :: [Lens a a Int Int] -> a -> (Int, a)
  87. bar :: Functor f => [LensLike f a a Int Int] -> a -> (Int, a)
  88. ```
  89. The first function takes a list of lenses; It can then use these lenses
  90. in any way it pleases. The second, however, takes a list of lens-like
  91. values _that all use the same functor_. Thus, you can't `view` using the
  92. head of the list and `over` using the second element! (Recall that
  93. `view` uses the `Const`{.haskell} functor and `over`{.amulet} the
  94. `Identity`{.amulet} functor). Indeed, the second function can't use the
  95. lenses at all, since it must work for an arbitrary functor and not
  96. `Const`{.haskell}/`Identity`{.haskell}.
  97. Of course, [Amulet lets you put lenses in lists]: See `lens_list` and
  98. `xs` at the bottom of the file.
  99. [^1]: Assume that the `'a`{.amulet} variable is bound by a `forall
  100. 'a.`{.amulet} quantifier. Since we don't use visible type application in
  101. the following example, I just skipped mentioning it.
  102. [Quick Look impredicativity]: https://github.com/serras/ghc-proposals/blob/quick-look/proposals/0000-quick-look-impredicativity.md
  103. [Amulet lets you put lenses in lists]: /static/profunctor-impredicative.ml.html