[Haskell-cafe] Dynamic typing of polymorphic functions
oleg at okmij.org
oleg at okmij.org
Thu Dec 20 05:47:51 EST 2007
Alfonso Acosta wrote:
> mapSY :: (Typeable a, Typeable b) => (a -> b) -> Signal a -> Signal b
> mapSY f (Signal primSig) = Signal (PrimSignal (MapSY (toDyn f) primSig))
>
> The following process would be really useful but its compilation
> obviously fails:
>
> mapSnd :: Signal (a, a) -> Signal a
> mapSnd = mapSY snd
>
>
> Could not deduce (Typeable a) from the context () arising from a
> use of `mapSY'
> Possible fix:
> add (Typeable a) to the context of the type signature for `mapSnd'
It seems the compiler's complaint is reasonable. The signature of the
mapSY function says that mapSY may only be applied _provided_ that type
variables 'a' and 'b' are instantiated to the types that are members
of Typeable. That is, mapSY has a condition on its use. When you
write
> mapSndInt :: Signal (Int, Int) -> Signal Int
> mapSndInt = mapSY (snd :: (Int, Int) -> Int)
the condition is satisfied: 'a' and 'b' are instantiated to Int, and
Int is a member of Typeable. The definition of mapSnd has no
constraint. The compiler is upset: mapSY requires a condition, and
mapSnd does not provide any, and there is no obvious way how an
obligation Typeable a could have been satisfied otherwise. So, writing
> mapSnd :: Typeable a => Signal (a, a) -> Signal a
> mapSnd = mapSY snd
is the logical thing to do.
> Well, strangely enough, adding the "Typeable a" constraint hushed GHC
> but an error was instead triggered at runtime.
Perhaps the latter is the real problem. If one switches to dynamic
typing, the type errors show up as run-time errors. I believe the
typing of eval is a bit odd (and also not very useful). The following
code seems to work. It also shows how to apply a polymorphic function,
pairing, to to signals of any type. Here's a test:
> signal3 = cons const0 (cons const0 const1)
*Foo> :t signal3
signal3 :: Signal (Int, (Int, Float))
> test1 = mapSnd signal3
test1 :: Signal (Int, Float)
> test12 = beval test1
*Foo> :t test12
test12 :: (Int, Float)
*Foo> test12
(0,1.0)
{-# OPTIONS -fglasgow-exts #-}
module Foo where
import Data.Typeable
import Data.Dynamic
-- the phantom type parameter makes signal typing consistent
newtype Signal a = Signal PrimSignal
newtype PrimSignal = PrimSignal (Proc (PrimSignal))
data Proc input = MapSY Dynamic -- The processing function
input -- The process input
-- the rest of the processes are omitted
| Const Dynamic
| Cons Dynamic input input
eval :: PrimSignal -> Dynamic
-- evaluates the output of a process for one input
eval (PrimSignal (MapSY dynF dynIn)) = dynApp dynF (eval dynIn)
eval (PrimSignal (Cons cns a1 a2)) = dynApp (dynApp cns (eval a1)) (eval a2)
eval (PrimSignal (Const inp)) = inp
-- better eval
beval :: Typeable a => Signal a -> a
beval (Signal s) = maybe undefined id (fromDynamic (eval s))
-- sample signals
const0 :: Signal Int
const0 = Signal (PrimSignal (Const (toDyn (0::Int))))
const1 :: Signal Float
const1 = Signal (PrimSignal (Const (toDyn (1::Float))))
-- the map process constructor
mapSY :: (Typeable a, Typeable b) => (a -> b) -> Signal a -> Signal b
mapSY f (Signal primSig) = Signal (PrimSignal (MapSY (toDyn f) primSig))
add1 :: Signal Int -> Signal Int
add1 = mapSY ((+1) :: Int -> Int)
mapSndInt :: Signal (Int, Int) -> Signal Int
mapSndInt = mapSY (snd :: (Int, Int) -> Int)
-- it is important to give the signature to (,) below: we pack the cons
-- function of the right type!
cons :: forall a b. (Typeable a, Typeable b) =>
Signal a -> Signal b -> Signal (a,b)
cons (Signal sig1) (Signal sig2) =
Signal (PrimSignal (Cons (toDyn ((,)::a->b->(a,b))) sig1 sig2))
mapSnd :: (Typeable a, Typeable b) => Signal (b, a) -> Signal a
mapSnd = mapSY snd
signal3 = cons const0 (cons const0 const1)
-- *Foo> :t signal3
-- signal3 :: Signal (Int, (Int, Float))
test1 = mapSnd signal3
-- test1 :: Signal (Int, Float)
test11 = let Signal s = test1 in eval s
-- *Foo> test11
-- <<(Int,Float)>>
-- Too bad. But we can do better.
test12 = beval test1
{-
*Foo> :t test12
test12 :: (Int, Float)
*Foo> test12
(0,1.0)
-}
More information about the Haskell-Cafe
mailing list