考虑
data Foo1 = Foo1 { f1 :: Int }
data Foo2 = Foo2 { f2 :: Int }
data Foo3 = Foo3 { f3 :: Int }
data Thing =
Thing1 Foo1
| Thing2 Foo2
| Thnig3 Foo3
bar :: Thing -> Int
bar (Thing1 foo1) = f1 foo1
bar (Thing2 foo2) = f2 foo2
..
这显然是疯了。
我该如何解决这个问题?
我试过
class Foo g where
f :: g -> Int
instance Foo (Foo1) where
f = f1
...
但这没有事件帮助,因为我仍然无法在没有所有模式匹配的情况下编写bar
。
我想要的是
bar :: Thing -> Int
bar (_ foo) = f foo
但这不可能。
(我可以根据foo的类型对Thing进行选择,然后它会正常工作;但我不能在我的实际代码中这样做;即我需要" Thing"完全是一般。我很困惑为什么我必须为FooK进行k次匹配模式,如果我已经定义了一些他们都在处理的类型类。)
答案 0 :(得分:4)
如果你想要一个通常在特定类型上使用存在但偶尔模式匹配的字段,那么你可以构建一个单例类型来将存在性恢复为具体类型。
{-# LANGUAGE GADTs #-}
data Foo1 = Foo1 { f1 :: Int }
data Foo2 = Foo2 { f2 :: Int }
data Foo3 = Foo3 { f3 :: Int }
class Foo g where
f :: g -> Int
-- A singleton type which introduces a unique constructor
-- for each type you want to store in the existential.
data SFoo t where
SFoo1 :: SFoo Foo1
SFoo2 :: SFoo Foo2
SFoo3 :: SFoo Foo3
-- The type parameter of the singleton matches with the existential.
-- This allows us to use pattern matching to find out the real type
-- of "t" later on.
data Thing where
Thing :: Foo t => SFoo t -> t -> Thing
-- Now you can use the existential through the type-class
bar :: Thing -> Int
bar (Thing _ foo) = f foo
-- And you can also pattern match on a specific constructor
-- when needed. Pattern matching on the singleton "SFoo1"
-- convinces the type-checker that the existential field
-- must have a type of "Foo1" which lets you use it normally.
bar2 :: Thing -> Maybe Int
bar2 (Thing SFoo1 (Foo1 i)) = Just i
bar2 _ = Nothing
答案 1 :(得分:1)
救援的存在资格类型!请注意,这不是Haskell98的一部分,因此您需要启用一个GHC类型的系统扩展。
在这里,我们定义Thing
能够保存任何类型的对象,该对象是Foo
类型类的实例。
{-# LANGUAGE ExistentialQuantification #-}
class Foo g where
f :: g -> Int
data Foo1 = Foo1 { f1 :: Int }
instance Foo (Foo1) where f = f1
data Thing = forall a . Foo a => Thing a
bar :: Thing -> Int
bar (Thing t) = f t
查看"异构列表"这里的例子: https://en.wikibooks.org/wiki/Haskell/Existentially_quantified_types
编辑1:
如果您需要能够提取特定FooN
,那么您似乎需要至少进行一次模式匹配。一种选择是进行一次模式匹配以从FooThing
构建通用Thing
,然后,如果可能,您可以使用FooThing
{-# LANGUAGE ExistentialQuantification #-}
class Foo g where
f :: g -> Int
data Foo1 = Foo1 { f1 :: Int }
instance Foo (Foo1) where f = f1
data Thing = Thing1 Foo1
data FooThing = forall a . Foo a => FooThing a
fooThing :: Thing -> FooThing
fooThing (Thing1 t) = FooThing t
bar :: Thing -> Int
bar = g . fooThing
where g (FooThing t) = f t
您甚至可以更进一步为Foo
Thing
的实例
instance Foo (Thing) where
f = g . fooThing
where g (FooThing t) = f t
编辑2:
在看到shang的回答之后,GADT可能是一种更好的方法,即使你只需要使用Thing Foo1
作为````Foo``,例如
{-# LANGUAGE GADTs #-}
class Foo g where
f :: g -> Int
data Foo1 = Foo1 { f1 :: Int }
instance Foo (Foo1) where f = f1
data Thing where
Thing :: Foo t => t -> Thing
bar :: Thing -> Int
bar (Thing foo) = f foo