假设我们有以下类型定义:
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 中解决此类案例的最自然和惯用的方法是什么?我试图避免过于复杂或样板的解决方案。
有一种我在实践中从未有机会使用的技术,即使用所使用的构造函数标记类型。这值得一试。
{-# 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)
我认为这也可能是矫枉过正,你只需要稍微不同地抽象。如有疑问,请尝试添加类型参数。
最自然和惯用的方法是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)