如何清理模式匹配表达式(或如何使用存在类型作为约束类和特定类型。)

时间:2014-10-19 04:25:28

标签: haskell

考虑

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次匹配模式,如果我已经定义了一些他们都在处理的类型类。)

2 个答案:

答案 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
相关问题