如果启用ConstraintKinds
和ExistentialQuantification
(或GADTs
),您可以具体化类型类字典:
{-# LANGUAGE ConstraintKinds, ExistentialQuantification #-}
data Dict a = a => Dict
如果我们试试这个
fibs :: Num n => [n]
fibs = 1 : 1 : zipWith (+) fibs (drop 1 fibs)
fibs' :: [Integer]
fibs' = fibs
fibsWithDict :: Dict (Num n) -> [n]
fibsWithDict Dict = fs
where
fs = 1 : 1 : zipWith (+) fs (drop 1 fs)
fibs'' :: [Integer]
fibs'' = fibsWithDict Dict
在 GHCi 中,我们看到
λ> :set +s
λ>
λ> fibs !! 29
832040
(2.66 secs, 721235304 bytes)
λ>
λ> fibs !! 29
832040
(2.52 secs, 714054736 bytes)
λ>
λ>
λ> fibs' !! 29
832040
(2.67 secs, 713510568 bytes)
λ>
λ> fibs' !! 29
832040
(0.00 secs, 1034296 bytes)
λ>
λ>
λ> fibs'' !! 29
832040
(0.00 secs, 1032624 bytes)
这fibs''
三个立即记忆的唯一实现也是如此。
请注意,我们必须在Dict
构造函数上进行模式匹配。否则,我们将收到一个关于n
不受约束的Num
实例的错误(就像您所期望的,如果我们的签名只是fibsWithDict :: a -> [n]
)。
这是一个完整的解决方案,因为您可以认为fibsWithDict Dict
它是一个表达式,可以立即记住您向它抛出的任何类型(只要它是 的实例Num
)。例如:
λ> (fibsWithDict Dict !! 29) :: Double
832040.0
(0.00 secs, 1028384 bytes)
编辑:看起来这里不需要显式的字典传递,可以通过使用ScopedTypeVariables
本地绑定来隐式完成:
{-# LANGUAGE ScopedTypeVariables #-}
fibsImplicitDict :: forall a. Num a => [a]
fibsImplicitDict
= let fs :: [a]
fs = 1 : 1 : zipWith (+) fs (drop 1 fs)
in
fs
(感谢 bennofs 提供的见解!)