Haskell 中的转换器和单态限制
Transducers in Haskell and the monomorphism restriction
我在 Haskell 中实现了如下转换器:
{-# LANGUAGE RankNTypes #-}
import Prelude hiding (foldr)
import Data.Foldable
type Reducer b a = a -> b -> b
type Transducer a b = forall t. Reducer t b -> Reducer t a
class Foldable c => Collection c where
insert :: a -> c a -> c a
empty :: c a
reduce :: Collection c => Transducer a b -> c a -> c b
reduce f = foldr (f insert) empty
mapping :: (a -> b) -> Transducer a b
mapping f g x = g (f x)
现在我想定义一个通用的 map
函数。因此我将上面的代码加载到 GHCi 中:
Prelude> :load Transducer
[1 of 1] Compiling Main ( Transducer.hs, interpreted )
Ok, modules loaded: Main.
*Main> let map = reduce . mapping
<interactive>:3:20:
Couldn't match type ‘Reducer t0 b1 -> Reducer t0 a1’
with ‘forall t. Reducer t b -> Reducer t a’
Expected type: (a1 -> b1) -> Transducer a b
Actual type: (a1 -> b1) -> Reducer t0 b1 -> Reducer t0 a1
Relevant bindings include
map :: (a1 -> b1) -> c a -> c b (bound at <interactive>:3:5)
In the second argument of ‘(.)’, namely ‘mapping’
In the expression: reduce . mapping
*Main> let map f = reduce (mapping f)
*Main> :t map
map :: Collection c => (a -> b) -> c a -> c b
所以我无法定义map = reduce . mapping
。但是,我可以定义 map f = reduce (mapping f)
.
我认为这个问题是由单态限制引起的。我真的很想写 map = reduce . mapping
而不是 map f = reduce (mapping f)
。因此,我有两个问题:
- 是什么导致了这个问题?真的是单态限制吗?
- 我该如何解决这个问题?
如果您将 Transducer
设为 newtype
,那么 GHC 会更好地计算类型。存在类型变量不会脱离作用域——转换器将保持多态。
换句话说,使用以下定义 map = reduce . mapping
有效
{-# LANGUAGE RankNTypes #-}
import Prelude hiding (foldr, map, (.), id)
import Control.Category
import Data.Foldable
type Reducer b a = a -> b -> b
newtype Transducer a b = MkTrans { unTrans :: forall t. Reducer t b -> Reducer t a }
class Foldable c => Collection c where
insert :: a -> c a -> c a
empty :: c a
instance Collection [] where
insert = (:)
empty = []
reduce :: Collection c => Transducer a b -> c a -> c b
reduce f = foldr (unTrans f insert) empty
mapping :: (a -> b) -> Transducer a b
mapping f = MkTrans $ \g x -> g (f x)
filtering :: (a -> Bool) -> Transducer a a
filtering f = MkTrans $ \g x y -> if f x then g x y else y
map :: Collection c => (a -> b) -> c a -> c b
map = reduce . mapping
filter :: Collection c => (a -> Bool) -> c a -> c a
filter = reduce . filtering
instance Category Transducer where
id = MkTrans id
MkTrans f . MkTrans g = MkTrans $ \x -> g (f x)
dub :: Num a => a -> a
dub x = x + x
test1 :: [Int]
test1 = reduce (filtering even . mapping dub) [1..10]
-- [2,4,6,8,10,12,14,16,18,20]
test2 :: [Int]
test2 = reduce (mapping dub . filtering even) [1..10]
-- [4,8,12,16,20]
*Main> :t reduce . mapping
reduce . mapping :: Collection c => (a -> b) -> c a -> c b
您也可以查看 http://www.reddit.com/r/haskell/comments/2cv6l4/clojures_transducers_are_perverse_lenses/
其中定义是 type Transducer a b =:: (a -> Constant (Endo x) a) -> (b -> Constant (Endo x) b)
和其他各种。还有其他有趣的讨论。
我在 Haskell 中实现了如下转换器:
{-# LANGUAGE RankNTypes #-}
import Prelude hiding (foldr)
import Data.Foldable
type Reducer b a = a -> b -> b
type Transducer a b = forall t. Reducer t b -> Reducer t a
class Foldable c => Collection c where
insert :: a -> c a -> c a
empty :: c a
reduce :: Collection c => Transducer a b -> c a -> c b
reduce f = foldr (f insert) empty
mapping :: (a -> b) -> Transducer a b
mapping f g x = g (f x)
现在我想定义一个通用的 map
函数。因此我将上面的代码加载到 GHCi 中:
Prelude> :load Transducer
[1 of 1] Compiling Main ( Transducer.hs, interpreted )
Ok, modules loaded: Main.
*Main> let map = reduce . mapping
<interactive>:3:20:
Couldn't match type ‘Reducer t0 b1 -> Reducer t0 a1’
with ‘forall t. Reducer t b -> Reducer t a’
Expected type: (a1 -> b1) -> Transducer a b
Actual type: (a1 -> b1) -> Reducer t0 b1 -> Reducer t0 a1
Relevant bindings include
map :: (a1 -> b1) -> c a -> c b (bound at <interactive>:3:5)
In the second argument of ‘(.)’, namely ‘mapping’
In the expression: reduce . mapping
*Main> let map f = reduce (mapping f)
*Main> :t map
map :: Collection c => (a -> b) -> c a -> c b
所以我无法定义map = reduce . mapping
。但是,我可以定义 map f = reduce (mapping f)
.
我认为这个问题是由单态限制引起的。我真的很想写 map = reduce . mapping
而不是 map f = reduce (mapping f)
。因此,我有两个问题:
- 是什么导致了这个问题?真的是单态限制吗?
- 我该如何解决这个问题?
如果您将 Transducer
设为 newtype
,那么 GHC 会更好地计算类型。存在类型变量不会脱离作用域——转换器将保持多态。
换句话说,使用以下定义 map = reduce . mapping
有效
{-# LANGUAGE RankNTypes #-}
import Prelude hiding (foldr, map, (.), id)
import Control.Category
import Data.Foldable
type Reducer b a = a -> b -> b
newtype Transducer a b = MkTrans { unTrans :: forall t. Reducer t b -> Reducer t a }
class Foldable c => Collection c where
insert :: a -> c a -> c a
empty :: c a
instance Collection [] where
insert = (:)
empty = []
reduce :: Collection c => Transducer a b -> c a -> c b
reduce f = foldr (unTrans f insert) empty
mapping :: (a -> b) -> Transducer a b
mapping f = MkTrans $ \g x -> g (f x)
filtering :: (a -> Bool) -> Transducer a a
filtering f = MkTrans $ \g x y -> if f x then g x y else y
map :: Collection c => (a -> b) -> c a -> c b
map = reduce . mapping
filter :: Collection c => (a -> Bool) -> c a -> c a
filter = reduce . filtering
instance Category Transducer where
id = MkTrans id
MkTrans f . MkTrans g = MkTrans $ \x -> g (f x)
dub :: Num a => a -> a
dub x = x + x
test1 :: [Int]
test1 = reduce (filtering even . mapping dub) [1..10]
-- [2,4,6,8,10,12,14,16,18,20]
test2 :: [Int]
test2 = reduce (mapping dub . filtering even) [1..10]
-- [4,8,12,16,20]
*Main> :t reduce . mapping
reduce . mapping :: Collection c => (a -> b) -> c a -> c b
您也可以查看 http://www.reddit.com/r/haskell/comments/2cv6l4/clojures_transducers_are_perverse_lenses/
其中定义是 type Transducer a b =:: (a -> Constant (Endo x) a) -> (b -> Constant (Endo x) b)
和其他各种。还有其他有趣的讨论。