Skip to content

Commit 9e7ecd2

Browse files
committed
rename gabriel -> gabriella
1 parent f7ca5e6 commit 9e7ecd2

File tree

3 files changed

+103
-10
lines changed

3 files changed

+103
-10
lines changed

copy/entries/faking-adts-and-gadts.md

+98-5
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,13 @@ all branches return the same type. Instead, the best you can do is have an
193193
returning a value. This is a good plan of action for languages like javascript
194194
(sans typescript), or python without types.
195195
196+
<!-- maybe task = idle | performing string -->
197+
198+
```javascript
199+
```
200+
201+
TODO: rewrite in javascript to hint at ffi example later
202+
196203
In languages without context-binding functions, you might also need to add a
197204
closure-simulating context into your visitor:
198205

@@ -664,10 +671,92 @@ displayNumericAxis = \case
664671
There are at least two main approaches to do this. We'll be discussing runtime
665672
equality witnesses and Higher-Kinded Eliminators.
666673

667-
#### Runtime Equality Witnesses
674+
#### Runtime Witnesses and Coyoneda Embedding
675+
676+
The [Yoneda Lemma][] is one of the most powerful tools that Category Theory has
677+
yielded as a branch of math, but its sibling [coyoneda][] is one of the most
678+
useful Haskell abstractions.
679+
680+
[Yoneda Lemma]: https://ncatlab.org/nlab/show/Yoneda+embedding
681+
[coyoneda]: https://hackage.haskell.org/package/kan-extensions/docs/Data-Functor-Coyoneda.html
682+
683+
This doesn't give you GADTs, but it's a very lightweight way to "downgrade"
684+
your GADTs into normal GADTs which is appropriate if you don't need the full
685+
power.
686+
687+
The trick is this: if you have `MyGADT a`, and you know you are going to be
688+
using it to _produce_ `a`s, you can do a covariant coyoneda transform.
689+
690+
For example, if you have this type representing potential data sources:
691+
692+
```haskell
693+
data Source :: Type -> Type where
694+
ByteSource :: Handle -> Source Word
695+
StringSource :: FilePath -> Source String
696+
697+
readByte :: Handle -> IO Word
698+
readString :: FilePath -> IO String
699+
700+
readSource :: Source a -> IO a
701+
readSource = \case
702+
ByteSource h -> readByte h
703+
StringSource fp -> readString fp
704+
```
705+
706+
You could instead turn `Source` into a non-GADT by making it a normal
707+
parameterized ADT and adding a `X -> a` field, which is a type of CPS
708+
transformation:
709+
710+
```haskell
711+
data Source a =
712+
ByteSource Handle (Word -> a)
713+
| StringSource FilePath (String -> a)
714+
715+
byteSource :: Handle -> Source Word
716+
byteSource h = ByteSource h id
717+
718+
stringSource :: FilePath -> Source String
719+
stringSource fp = StringSource fp id
720+
721+
readSource :: Source a -> IO a
722+
readSource = \case
723+
ByteSource h out -> out <$> readByte h
724+
StringSource fp out -> out <$> readString fp
725+
```
726+
727+
A nice benefit of this method is that `Source` can now have a `Functor`
728+
instance, which the original GADT could not.
729+
730+
And, if `MyGADT a` is going to be _consuming_ `a`s, you can do the [contravariant
731+
coyoneda][] transform:
732+
733+
[contravariant coyoneda]: https://hackage.haskell.org/package/kan-extensions/docs/Data-Functor-Contravariant-Coyoneda.html
734+
735+
```haskell
736+
data Sink a =
737+
ByteSink Handle (a -> Word)
738+
| StringSink FilePath (a -> String)
739+
```
740+
741+
This gives it a free [Contravariant][] instance too!
742+
743+
[Contravariant]: https://hackage.haskell.org/package/base/docs/Data-Functor-Contravariant.html
668744

669-
Essentially what we need is something "inside" `NInt` that, when we pattern
670-
match on a `NType a`, the type system can be assured that `a` is an `Int`.
745+
And, if you are going to be both consuming and producing `a`s, you can do the
746+
*invariant coyoneda* transform
747+
748+
```haskell
749+
data Interface a =
750+
ByteInterface Handle (Word -> a) (a -> Word)
751+
| StringInterface FilePath (String -> a) (Word -> a)
752+
```
753+
754+
However, in practice, _true equality_ involves being able to lift under
755+
injective type constructors, and carrying _every single_ continuation is
756+
unwieldy. We can package them up together with a **runtime equality witness**.
757+
758+
This is something we can put "inside" `NInt` such that, when we pattern match
759+
on a `NType a`, the type system can be assured that `a` is an `Int`.
671760

672761
You need some sort of data of type `IsEq a b` with functions:
673762

@@ -679,8 +768,12 @@ You need some sort of data of type `IsEq a b` with functions:
679768

680769
If you have `to` and `sym` you also get `from :: IsEq a b -> b -> a`.
681770

682-
Your language of choice might have this. But one of the more interesting ways
683-
to me is Leibniz equality (discussed a lot in [this Ryan Scott
771+
From all of this, we can recover our original `IsEq a Word -> Word -> a` and
772+
`IsEq a Word -> a -> Word` functions, saving us from having to put two
773+
functions.
774+
775+
Your language of choice might already have this `IsEq`. But one of the more
776+
interesting ways to me is Leibniz equality (discussed a lot in [this Ryan Scott
684777
post][ryanglscott]), which works in languages with higher-kinded polymorphism.
685778
Leibniz quality in languages with higher-kinded polymorphism means that `a` and
686779
`b` are equal if `forall p. p a -> p b`: any property of `a` is also true of

copy/entries/inside.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -943,9 +943,9 @@ Haskell lets me stay *inside my world*!
943943
Final Notes
944944
-----------
945945

946-
For some further reading, Gabriel Gonzalez's ["Functor Design Pattern"][tekmo]
947-
post covers a similar concept for people more familiar with Haskell and
948-
explains it more elegantly than I ever could have.
946+
For some further reading, Gabriella Gonzalez's ["Functor Design
947+
Pattern"][tekmo] post covers a similar concept for people more familiar with
948+
Haskell and explains it more elegantly than I ever could have.
949949

950950
Don't forget as you're reading and moving on that it's not
951951
correct to say "Functors are worlds", or "Monads are worlds". As I mentioned

copy/entries/machines-3.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -573,8 +573,8 @@ all have access to a global state.
573573

574574
What if we wanted the best of both worlds? What would that look like?
575575

576-
In Haskell, one common technique we like to use, eloquently stated by Gabriel
577-
Gonzalez in his post [the Functor design pattern][tekmo], is to pick a "common
576+
In Haskell, one common technique we like to use, eloquently stated by Gabriella
577+
Gonzalez in her post [the Functor design pattern][tekmo], is to pick a "common
578578
denominator" type, and push all of our other types into it.
579579

580580
[tekmo]: http://www.haskellforall.com/2012/09/the-functor-design-pattern.html

0 commit comments

Comments
 (0)