@@ -119,7 +119,19 @@ No. This behavior is intrinsically fixed! We got this theorem _for free_.
119119Without any need for any sort of works . We didn ' t even have to _write_ the
120120function before knowing all it possibly could be.
121121
122- Consider another type signature:
122+ This is the power of the `forall`, for you. Note that the above `foo :: a -> a`
123+ is "sugar" for:
124+
125+ ```haskell
126+ foo :: forall a. a -> a
127+ ```
128+
129+ If you don't add a `forall a`, it is implicitly added. Some languages, like
130+ purescript and dhall, require the `forall` in every case to be explicit. This is
131+ basically Haskell's equivalent of `<T >` in java, `template <typename T >` in
132+ C++, etc.
133+
134+ Anyway, let's consider another type signature:
123135
124136```java
125137static <T > String foo(T x )
@@ -220,6 +232,7 @@ What could this do?
220232Well , we know that all items from the result list _must_ be from the input
221233list. It must be a subset --- but the ordering can change, or the multiplicity.
222234And , more importantly, it _can't_ depend anything on the properties of any `a` .
235+ We also know that if the input is empty, so must be the output.
223236
224237From this, we can derive what are called [free theorems][] to look at
225238properties that _any implementation_ must take . Namely , _mapping_ a function
@@ -263,7 +276,37 @@ And again we have the same free theorem, `doIt . map f === fmap f . doIt`. No
263276matter how you implement `doIt` , it is guaranteed to commute with `map` and
264277`fmap` !
265278
266- ### Surrendering Control
279+ Let's try another one:
280+
281+ ```haskell
282+ collapse :: [a ] -> Int
283+ ```
284+
285+ What could this possibly do ? Well , we can rule out things like `sum` because we
286+ can't use any property of the values itself. The only things that this could
287+ return are constant functions and functions that depend on the _length_ but not
288+ the _contents_ of the list. We also have another free theorem, `collapse . map
289+ f == collapse`: mapping a function shouldn't change the output, because none of
290+ the actual values matter.
291+
292+ One final one, with a higher- rank variable:
293+
294+ ```haskell
295+ traverseIO :: (a -> IO b ) -> [a ] -> IO [b ]
296+
297+ -- vs
298+
299+ traverse :: Applicative f => (a -> f b ) -> [a ] -> f [b ]
300+ ```
301+
302+ What invariant does the second add over the first? Even _if_ you only ever plan
303+ on calling things with `IO `, the second gives you a new invariant: there won't
304+ be any " stray" `IO ` actions other than what is given in the `a -> f b`. In the
305+ first one, you never know if the resulting `IO ` action might include a
306+ `putStrLn " hello" ` or a `launchMissiles` . You definitely don't any functions
307+ doing sneaky IO behind your back!
308+
309+ ### The More you Surrender
267310
268311Practically , this becomes similar to the principal of least power. Say you
269312_are_ writing a function that shuffles a list of items, important for your
@@ -290,8 +333,8 @@ explicitly declare a post-condition like "the final values must all come from
290333the original list" . With parametric polymorphism, this is already guaranteed
291334and elected, no matter what the implementation is.
292335
293- Preserving Structures
294- ---------------------
336+ Add a Type Variable
337+ -------------------
295338
296339Let's say I had a data type like:
297340
@@ -419,6 +462,177 @@ doesn't depend on any foreseen property of the types or values we use. These
419462guarantees free us to be able to confidently use these functions without
420463fear of invariants breaking.
421464
465+ This game becomes even stronger when you consider dependent typing, where we
466+ can express more complex relationships between type variables. For example, in
467+ the case where you have a phantom type (like in [this singletons
468+ tutorial][singletons]):
469+
470+ [singletons]: https:// blog. jle. im/ entry/ introduction- to- singletons- 1 . html
471+
472+ ```haskell
473+ data DoorState = Opened | Closed | Locked
474+
475+ data Door (s :: DoorState )
476+
477+ processDoor :: Door s -> IO (Door s )
478+ ```
479+
480+ `processDoor` , by nature of taking `forall s`, _must leave_ the door state
481+ unchanged! It can never open a closed door, unlock a locked door, etc.
482+
483+ For things like [fixed length vectors][vectors], where the length `n` parameter
484+ is the size, what invariant do you think is preserved in :
485+
486+ [vectors]: https:// blog. jle. im/ entry/ fixed- length - vector- types- in - haskell. html
487+
488+ ```haskell
489+ something :: Vector n a -> Vector n a
490+ ```
491+
492+ We know that the length of the result must be the same as the length of the
493+ input. Furthermore , with the `forall a`, we know that every item in the result
494+ must come from the input, but we might rearrange or change the multiplicity of
495+ the occurrences as long as they add to the same original total number. This
496+ might be a good candidate for a function like `reverse` .
497+
498+ Or , consider:
499+
500+ ```haskell
501+ somethingElse :: Vector n a -> Vector (n - 1 ) a
502+ ```
503+
504+ From this, we know that the original vector _must_ be non- empty! Because of how
505+ flow of the types must work for whatever `n` you give it, this flow requires
506+ `n - 1 > 0 ` and so `n > 1 `.
507+
508+ Ranking Up
509+ ----------
510+
511+ Now that you see how useful it is to utilize type parameters and `forall` , can
512+ we _use_ this fact at the meta- level even within our code itself?
513+
514+ Let's say we want to map an IO function over every item in our `UserF `, and
515+ return a new one. We know that whatever IO function we use _must_ leave the
516+ actual " result" type unchanged. So that means we must take a `forall a. f a ->
517+ IO (f a)`
518+
519+ ```haskell
520+ traverseUser
521+ :: Applicaive h
522+ => (forall a . f a -> h (g a ))
523+ -> UserF f
524+ -> h (UserF g )
525+ traverseUser f u = UserF <$> f (userName u) <*> f (userAge u)
526+ ```
527+
528+ Here again we use the trick above to generalize for all ` Applicative h ` instead
529+ of concretely ` IO ` , so we can know that the final action can't sneak in stray
530+ IO.
531+
532+ We can also use this property in phantom types to enforce memory regions.
533+ Let's say we are simulating memory in an ` IntMap ` :
534+
535+ ``` haskell
536+ newtype Var = Var Int
537+ newtype Memory v = Memory { getMemory :: IntMap v }
538+
539+ initVar :: v -> State (Memory v ) Var
540+ initVar x = state $ \ (Memory mp) ->
541+ case IM. lookupMax mp of
542+ Nothing -> (Var 0 , Memory $ IM. insert 0 x mp)
543+ Just i -> (Var (i + 1 ), Memory $ IM. insert (i + 1 ) x mp)
544+
545+ readVar :: Var -> State (Memory v ) v
546+ readVar (Var i) = gets ((IM. ! i) . getMemory)
547+
548+ writeVar :: Var -> v -> State (Memory v ) ()
549+ writeVar (Var i) x = modify (Memory . IM. insert i x . getMemory)
550+
551+ runWithMemory :: State (Memory v ) a -> a
552+ runWithMemory = (`evalState` Memory IM. empty)
553+ ```
554+
555+ (By the way, what do we gain from having the state be ` IntMap v ` parametric?
556+ What guarantees/invariants do we get, what sort of actions do we forbid the
557+ library itself from doing? Is it possible to have an default-initialized
558+ variable?)
559+
560+ We can run operations like:
561+
562+ ``` haskell
563+ getFib :: Int -> State (Memory v ) Int
564+ getFib n = do
565+ a <- initVar 0
566+ b <- initVar 1
567+ replicateM_ n $ do
568+ newSum <- (+) <$> readVar a <*> readVar b
569+ writeVar a =<< readVar b
570+ writeVar b newSum
571+ readVar b
572+ ```
573+
574+ ``` haskell
575+ ghci> runWithMemory (getFib 10 )
576+ 55
577+ ```
578+
579+ But now our memory regions are pretty unsafe. We could, for instance, do:
580+ run ` runWithMemory ` _ inside_ itself:
581+
582+ ``` haskell
583+ myAction :: State (Memory v ) a
584+ myAction = do
585+ v <- initVar " oneRegion"
586+ let x = runWithMemory $ do
587+ readVar v
588+ -- ..
589+ -- ..
590+ ```
591+
592+ And now that ` readVar v ` makes no sense!
593+
594+ We can also do something silly like returning a ` Var ` :
595+
596+ ``` haskell
597+ ghci> runWithMemory (initVar " hello" )
598+ Var 0
599+ ```
600+
601+ And now that var exists without a context...its memory is gone, it refers to
602+ something that no longer has any meaning.
603+
604+ We can prevent this by associating a variable and its associated memory region
605+ with a phantom. Then we can ensure that ` runWithMemory ` requires the phantom to
606+ never be a part of the final output:
607+
608+ ``` haskell
609+ newtype Var s = Var Int
610+ newtype Memory s v = Memory { getMemory :: IntMap v }
611+
612+ initVar :: v -> State (Memory s v ) (Var s )
613+
614+ readVar :: Var s -> State (Memory s v ) v
615+
616+ writeVar :: Var s -> v -> State (Memory s v ) ()
617+
618+ runWithMemory :: (forall s . State (Memory s v ) a ) -> a
619+ runWithMemory = (`evalState` Memory IM. empty)
620+ ```
621+
622+ Here, a ` Var s ` must come from a memory bank ` Memory s v ` with the _ same_ ` s ` .
623+ It is associated with that region, and no others. The ` forall ` here ensures
624+ that the action being given cannot unify with any external ` s ` : it _ must_ be
625+ treated as fresh from ` runWithMemory. `
626+
627+ Right off the bat, this prevents passing variables into nested calls (the first
628+ var's ` s ` is different than the inner memory bank's ` s ` ), but this also
629+ prevents variables from leaking. That's because the result type ` a ` must be
630+ fully _ independent_ of the ` s ` , so returning a ` Var s ` is illegal, since that
631+ would require the ` a ` to depend on ` s ` .
632+
633+ By requiring the user to give up control of the ` s ` , we ensure safety both of
634+ the library and of the user-given
635+
422636<!-- bump abstraction, updator -->
423637
424638<!-- * Parametric polymorphism --- guess the implementation -->
0 commit comments