我正在玩轻量级的匿名唱片,更多的是探索它们的类型理论而不是任何工业实力'。我希望字段只是类型标记。
myRec = (EmpId 54321, EmpName "Jo", EmpPhone "98-7654321") -- in which
newtype EmpPhone a = EmpPhone a -- and maybe
data EmpName a where EmpName :: IsString a => a -> EmpName a -- GADT
data EmpId a where EmpId :: Int -> EmpId Int -- GADT to same pattern
虽然我可以放newtype EmpId = EmpId Int
,但我想对所有标签使用相同的模式,以便我可以举例如下:
project (EmpId, EmpName) myRec -- use tags as field names
我还会StandaloneDeriving/DeriveAnyType
使用derive instance Eq, Show, Num
等。
其他可能的设计
HList
或使用我自己的数据类型Tuple0, Tuple1, Tuple2, ...
而不是Haskell元组。我认为这不会影响下面的输入问题。对于代码/字段,我可以将Symbol
(类型级String
)作为幻像类型与值配对 - 例如CTRex
执行类似的操作。然后使用TypeApplications
构建字段。
data Tag (tag :: Symbol) a = Tag a
myRec = (Tag @"EmpId" 54321, ...)
这使得字段语法(和投影列表)相当'嘈杂&#39 ;;还会阻止EmpId
为Int
等的任何验证
有关输入的问题的三个相关行:
如何最好地预防
sillyRec = (EmpId 65432, Just "not my tag", "or [] as constructor",
Right "or even worse" :: Either Int String)
我可以声明一个类,只在其中放置我的标签(对DeriveAnyClass
来说也不错),在任何地方放置约束。但是我的标签具有一致的结构:单个数据构造函数与类型相同;单一类型参数,它是数据构造函数的唯一参数。
如何表达我希望每个记录都遵循一致的类型模式?这是预防:
notaRec = (EmpId 76543, EmpName)
Bare EmpName
在投影列表中没问题,提供所有其他字段都是裸构造函数。我想说notaRec
不是很好 - Kind
,但裸EmpName
是Kind * -> *
,这与*
是一致的。所以我的意思更像是:记录中的所有字段都符合相同的类型模式。
然后当我到达记录集(又名表/关系)
myTable = ( myRec, -- tuple of tuples
(EmpName "Kaz", EmpPhone 987654312, EmpId 87654),
EmpId 98765, EmpPhone "21-4365879", EmpName "Bo")
将字段放在不同的顺序是可以的,因为我们有一个元组元组。但EmpPhone
在两个记录中有两种不同的类型。而最后一行根本就没有记录:它的字段是错误的'图案。 (与2中的裸EmpName
相同的错误匹配。)
我想再次说这些病了 - Kind
。我的字段标记出现在不同的深度'或者是不同类型的模式。
我想我可以通过大量硬编码来实现类型的有效实例/组合。有更通用的方法吗?
编辑:回复评论。 (是的,我也是凡人。感谢@duplode弄清楚格式化。)
为什么不
type Record = (EmpId Int, EmpName String, EmpPhone String)
?
作为一个类型的同义词,很好。但是没有回答这个问题,因为我希望它等同于这些标签的任何排列。 (我想我可以使用HList
技术验证类型级别的等效性。)
对您的目标的某种高级概述[谢谢大卫]
我想将( ... , ... , ... )
视为一组。因为关系数据库模型表明关系是一组元组' [不是Haskell元组]和'元组'是一组标签值。我还想将project
函数视为具有第一类参数,该参数是一组标记。 (相比之下,在Codd的关系代数中,π
运算符的下标标签就好像是运营商的一部分。)
这些不能是Haskell Set
,因为元素不是同一类型。我想说元素是相同的Kind
;并且相同的Haskell元组 - Kind
ed元素表示一组Kind
。但我知道这是滥用术语。 (我考虑使用Symbol
标记的替代设计可能会更好地显示Kind
方面。)
如果我可以将Haskell元组视为set-ish,我可以使用众所周知的HList
技术来模拟关系运算符。
如果这有助于解释,我可以用很多样板来做到这一点:
class MyTag a -- type/kind-level predicate
deriving instance MyTag (EmpId Int) -- uses DeriveAnyClass
-- etc for all my tags
class WellKinded tup
instance WellKinded ()
instance {-# OVERLAPPING #-}
(MyTag (n1 a1), MyTag (n2 a2), MyTag (n3 a3))
=> WellKinded (n1 a1, n2 a2, n3 a3) -- and so on for every arity of tuple
instance {-# OVERLAPPABLE #-}
(MyTag (n1 a1), MyTag (n2 a2), MyTag (n3 a3))
=> WellKinded (a1 -> n1 a1, a2 -> n2 a2, a3 -> n3 a3)
所有针对不同城市的实例都会很快变得乏味,所以我可以转换为HList
;在第一个元素的Kind
上发送一个实例;迭代列表,验证所有相同的Kind
。
对于tuple-of-tuples,检测第一个子元组的第一个元素的Kind
;横向和向下迭代。 (再次需要OverlappingInstances
:元组的元组仍然是一个元组。这就是我所说的"大量的硬编码"以上。)它没有&# 39;似乎无法实现。但它确实感觉像走错了兔子洞。
答案 0 :(得分:0)
这是对2,3的回答或至少解释。对...的部分答案。
- 醇>
如何表达我希望每个记录都遵循一致的类型模式?这是预防:
notaRec = (EmpId 76543, EmpName)
表面上EmpId 76543
匹配类型模式(n a)
;而EmpName :: a -> (n a)
。但是欣德利 - 米尔纳并不像这样简单地“匹配”,它使用了统一性。因此,所有这些都与(n a)
:
-- as `( n a )`
a -> (n a) -- as `( ((->) a) (n a) )`
(b, c) -- as `( (,) b ) c `
(b, c, d) -- as `( (,,) b c ) d ` -- etc for all larger Haskell tuples
[ a ], Maybe a -- as `( [] a )`, `( Maybe a )`
Either b c -- as `( (Either b) c )`
b -> (Either b c) -- as `( ((->) b) (Either b c) )` -- for example, bare `Left`
在滥用术语方面不同意我自己:
我想说这些都是不健康的。我的字段标签出现在不同的“深度”...
但我知道这是滥用术语。
具有->
最外层构造函数的任何类型都与{1}}不同。 Kind
处于不同的Either
vs Kind
,因为它是不同的。类型统一构建' m ost g 通常 u nifier',这使它们显得相同 - EmpId
ed。
出于此目的,我们希望与mgu相反 - 将其称为“最大特定Kind
”,简称为Kind
。
我们可以用封闭的类型族和许多重叠方程来表达它(因此它们的顺序是关键的)。这也可以捕获不应该计算的Prelude的构造函数:
MaSK
限制:此方法无法检查该类型的单个数据构造函数,也不能检查该类型的其他构造函数是否与模式匹配,也不能将构造函数命名为与类型相同,也不能检查构造函数是否存在于存在性中 - 量化参数。为此,请使用全金属type family MaSK ( a :: * ) where
-- presume the result is one from some pre-declared bunch of types
-- use that result to verify all 'elements' of a set are same-kinded
MaSK (_ -> _ _ _) = No -- e.g. bare `Left`
MaSK (_ -> [ _ ]) = No -- reject unwanted constructors
MaSK (_ -> Maybe _ ) = No -- ditto
MaSK (a' -> n a') = YesAsBareTag -- this we want
MaSK (_ -> _ _ ) = No --
MaSK (_ -> _ ) = No
MaSK ( _ , _ , _ , _ ) = YesAsSet -- etc for greater arities
MaSK ( _ , _ , _ ) = YesAsSet
MaSK ( _ , _ ) = YesAsSet
MaSK (_ _ _ ) = No -- too much arity, e.g. `Either b c`
MaSK [ _ ] = No -- reject unwanted constructors
MaSK (Maybe _) = No -- ditto
MaSK (n a) = YesAsTagValue -- this we want providing all the above eliminated
MaSK _ = No -- i.e. bare `Int, Bool, Char, ...`
。
答案 1 :(得分:0)
这很疯狂,它可能会起作用。拯救的模式同义词:
newtype Label (n :: Symbol) (a :: *) = MkLab a -- newtype yay!
deriving (Eq, Ord, Show)
pattern EmpPhone x = MkLab x :: Label "EmpPhone" a
pattern EmpName x = MkLab x :: IsString a => Label "EmpName" a
pattern EmpId x = MkLab x :: Label "EmpId" Int
myRec = (EmpId 54321, EmpName "Jo", EmpPhone "98-7654321") -- works a treat
然后回答q的
要计为记录,所有元组元素必须为Label s a
类型。
要计为投影列表,所有元组元素必须为a -> Label s a
类型。
(那顺便说一下。)
这些是元组记录中允许的唯一类型/种类。
因此,要在类型级别解析元组元组,我只需要发送最左边元素的类型。
我正在寻找类型构造函数Label
。
我可以用HList
- 样式类型匹配来完成所有其他工作。
对于那些模式我确实需要打开一大堆扩展:
{-# LANGUAGE PatternSynonyms,
KindSignatures, DataKinds,
ScopedTypeVariables, -- for the signatures on patterns
RankNTypes #-} -- for the signatures with contexts
import GHC.TypeLits -- for the Symbols