... | ... | @@ -40,22 +40,22 @@ Now consider the following class: |
|
|
|
|
|
```wiki
|
|
|
class IsLabel (x :: Symbol) a where
|
|
|
fromLabel :: a
|
|
|
fromLabel :: Proxy# x -> a
|
|
|
```
|
|
|
|
|
|
|
|
|
Exactly like `IP` but without the functional dependency. It is also rather similar to a version of the `IsString` class from `OverloadedStrings`, but with an additional parameter making the string available at the type level.
|
|
|
Exactly like `IP` but without the functional dependency, and with an extra proxy argument. It is also rather similar to a version of the `IsString` class from `OverloadedStrings`, but with an additional parameter making the string available at the type level.
|
|
|
|
|
|
|
|
|
It behaves like this:
|
|
|
|
|
|
- When you write `#x` in an expression, what GHC does is to replace it with `(fromLabel @ "x" @ alpha)`, where `alpha` is a unification variable and `@` is type application. Just like implicit parameters, in fact.
|
|
|
- When you write `#x` in an expression, what GHC does is to replace it with `(fromLabel @"x" @alpha proxy#)`, where `alpha` is a unification variable and `@` is type application. Just like implicit parameters, in fact.
|
|
|
|
|
|
- Of course the call `(fromLabel @ "x" @ alpha)` gives rise to a constraint `(IsLabel "x" alpha)` which must be satisfied by the context.
|
|
|
- Of course the call `(fromLabel @"x" @alpha proxy#)` gives rise to a constraint `(IsLabel "x" alpha)` which must be satisfied by the context.
|
|
|
|
|
|
- The form `#x` in an expression is only valid with `{-# LANGUAGE OverloadedLabels #-}` (which is implied by `OverloadedRecordFields`).
|
|
|
- The form `#x` in an expression is only valid with `{-# LANGUAGE OverloadedLabels #-}` (which will be implied by `OverloadedRecordFields`).
|
|
|
|
|
|
- The pretty printer could print `IsLabel "x" t` as `#x::t` (**AMG**: I don't plan to implement this initially).
|
|
|
- The pretty printer could print `IsLabel "x" t` as `#x::t` (but it doesn't, yet).
|
|
|
|
|
|
- There is no functional dependency, and no equivalent to the implicit-parameter `let ?x=e` binding. So overloaded labels are much less special than implicit parameters.
|
|
|
|
... | ... | @@ -63,11 +63,7 @@ It behaves like this: |
|
|
Notice that overloaded labels might be useful for all sorts of things that are nothing to do with records; that is why they don't mention "record" in their name.
|
|
|
|
|
|
|
|
|
User code can never (usefully) call `fromLabel` (or `ip`) directly, because without explicit type application there is no way to fix `x`.
|
|
|
|
|
|
*Lennart*: I don't like the type of `fromLabel`. I'd much rather it had type `fromLabel :: Proxy# x -> a`, because then it can be used without the explicit type application extension.
|
|
|
|
|
|
*Adam*: I tend to agree, and I've brought this up on [ Phab:D1331](https://phabricator.haskell.org/D1331).
|
|
|
User code can call `fromLabel` directly (unlike `ip`), thanks to the proxy argument. When we have explicit type application we could consider dropping the argument again.
|
|
|
|
|
|
### Syntax
|
|
|
|
... | ... | @@ -100,6 +96,9 @@ Note that the `#x` form only behaves specially if you have `OverloadedLabels` or |
|
|
|
|
|
The downside of the `#x` syntax is that uses of lenses like `foo^.bar.baz` become something like `foo ^. #bar . #baz` or `foo ^. xx #bar . xx #baz` (if we need a combinator `xx` to turn an implicit value into a lens). However, this can be mitigated to some extent by users by making their own definitions `bar = xx #bar; baz = xx #baz`.
|
|
|
|
|
|
|
|
|
Sadly the `#x` syntax clashes with [ hsc2hs](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/hsc2hs.html#idp35055056), so users will have to write `##x` in `.hsc` files. But we don't see a better alternative.
|
|
|
|
|
|
### Reflections
|
|
|
|
|
|
|
... | ... | |