@@ -193,6 +193,13 @@ all branches return the same type. Instead, the best you can do is have an
193
193
returning a value. This is a good plan of action for languages like javascript
194
194
(sans typescript), or python without types.
195
195
196
+ <!-- maybe task = idle | performing string -->
197
+
198
+ ```javascript
199
+ ```
200
+
201
+ TODO: rewrite in javascript to hint at ffi example later
202
+
196
203
In languages without context-binding functions, you might also need to add a
197
204
closure-simulating context into your visitor:
198
205
@@ -664,10 +671,92 @@ displayNumericAxis = \case
664
671
There are at least two main approaches to do this. We'll be discussing runtime
665
672
equality witnesses and Higher-Kinded Eliminators.
666
673
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
668
744
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 `.
671
760
672
761
You need some sort of data of type `IsEq a b` with functions:
673
762
@@ -679,8 +768,12 @@ You need some sort of data of type `IsEq a b` with functions:
679
768
680
769
If you have `to` and `sym` you also get `from :: IsEq a b -> b -> a `.
681
770
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
684
777
post][ryanglscott]), which works in languages with higher- kinded polymorphism.
685
778
Leibniz quality in languages with higher- kinded polymorphism means that `a` and
686
779
`b` are equal if `forall p. p a -> p b`: any property of `a` is also true of
0 commit comments