Haskell 联积和数据记录字段限制

abd*_*nce 2 haskell

假设我们有以下类型定义:

data P = PA | PB
Run Code Online (Sandbox Code Playgroud)

(与PA或PB的结构无关)

然后,我们可以像这样定义其他一些记录类型(案例 1):

data C1 = C1 { field :: P } -- this is fine
Run Code Online (Sandbox Code Playgroud)

但有时您需要定义更受约束的字段类型(案例 2):

data C2 = C2 { field :: PA } -- won't compile because PA is a data constructor
Run Code Online (Sandbox Code Playgroud)

Haskell 中解决此类案例的最自然和惯用的方法是什么?我试图避免过于复杂或样板的解决方案。

luq*_*qui 8

有一种我在实践中从未有机会使用的技术,即使用所使用的构造函数标记类型。这值得一试。

{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}

data Tag = TA | TB

-- data P = PA | PB
data P t where
    PA :: P TA
    PB :: P TB
Run Code Online (Sandbox Code Playgroud)

然后,P t如果您的函数适用于任何构造函数,则可以采用一般性,但您也可以约束:

f :: P t -> Bool
f PA = True
f PB = False

g :: P TA -> ()
g PA = ()
-- PB case is unnecessary (and impossible to write)
Run Code Online (Sandbox Code Playgroud)

如果你想返回带有不确定标签的东西,这会有点尴尬。您可以使用存在的

data AnyP where
    AnyP :: P t -> AnyP

h :: Bool -> AnyP
h True = AnyP PA
h False = AnyP PB
Run Code Online (Sandbox Code Playgroud)

或者您可以使用存在项的“CPS”编码来避免引入新类型:

h' :: Bool -> (forall t. P t -> z) -> z
h' True c = c PA
h' False c = c PB
Run Code Online (Sandbox Code Playgroud)

我认为这也可能是矫枉过正,你只需要稍微不同地抽象。如有疑问,请尝试添加类型参数。


Fyo*_*kin 7

最自然和惯用的方法是PA(并且可能PB)创建自己的类型:

data PAtype = ...
data PBtype = ...
data P = PA PAtype | PB PBtype

data C1 = C1 { field :: P }
data C2 = C2 { field :: PAtype }
Run Code Online (Sandbox Code Playgroud)