I have updated the reflection package on hackage to use an idea for avoiding dummy arguments posted to the Haskell cafe mailing list by Bertram Felgenhauer, which adapts nicely to the case of handling Reflection. The reflection package implements the ideas from the Functional Pearl: Implicit Configurations paper by Oleg Kiselyov and Chung-chieh Shan.
Now, you no longer need to use big scary undefineds throughout your code and can instead program with implicit configurations more naturally, using Applicative and Monad sugar.
*Data.Reflection> reify (+)
(reflect < *> pure 1 < *> (reflect < *> pure 2 < *> pure 3))
6
The Monad in question just replaces the lambda with a phantom type parameter, enabling the compiler to more readily notice that no instance can actually even try to use the value of the type parameter.
An example from the old API can be seen on the Haskell cafe.
This example can be made appreciably less scary now!
{-# LANGUAGE
MultiParamTypeClasses,
FlexibleInstances, Rank2Types,
FlexibleContexts, UndecidableInstances #-}
import Control.Applicative
import Data.Reflection
import Data.Monoid
import Data.Tagged
newtype M s a = M a
instance Reifies s (a,a → a → a) ⇒ Monoid (M s a) where
mempty = tagMonoid $ fst < $> reflect
a `mappend` b = tagMonoid $
snd < $> reflect < *> monoidTag a < *> monoidTag b
monoidTag :: M s a → Tagged s a
monoidTag (M a) = Tagged a
tagMonoid :: Tagged s a → M s a
tagMonoid (Tagged a) = M a
withMonoid :: a → (a → a → a) →
(∀s. Reifies s (a, a → a → a) ⇒ M s w) → w
withMonoid e op m = reify (e,op) (monoidTag m)
And with that we can cram a Monoid dictionary -- or any other -- with whatever methods we want and our safety is assured by parametricity due to the rank 2 type, just like with the ST monad.
*> withMonoid 0 (+) (M 5 `mappend` M 4 `mappend` mempty)
9
[Edit: factored Tagged out into Data.Tagged in a separate package, and modified reflection to use that instead, with an appropriate version bump to satisfy the package versioning policy]
Discussion
@Sebastian:
Actually, I went through and tried to minimize the number of language extensions the package required. Two of the ones I eliminated were
EmptyDataDeclsandKindSignatures, since you can define a ‘bottom only’ datatype with anewtypewrapper, and you can perform some shenanigans to avoid the kind signatures once it is anewtype.However, when you don’t use the
newtype’s constructor anywhere,-Wallgives you a warning. Rather than have the module spit out 8-9 warnings, I wrote theUnusedtypeclass to give a single reference to each constructor and to silence the compiler.@Cale:
Yeah, it seems to be a bug in the package I’m using for syntax highlighting. =/ I swapped it out like 3 times, and eventually just moved on. If you have a suggestion for a good WordPress syntax highlighter that does Haskell, I’m all ears.
@Sjoerd:
The problem with that is that
Monoidisn’t the only kind of thing we might want to reify into a type so we can reflect it as a dictionary.If any other type had the same dictionary shape, and you took that approach then you could very easily conflate instances.
If the reflection library wanted to concern itself with every type class in Haskell, that would work fine, but otherwise, you would be stuck with orphan instances. ;)