我不确定这是否是正确的术语,但是是否可以声明接受数据类型“联合”的函数类型?
例如,我知道我可以执行以下操作:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
...
data Shape'
= Circle'
| Square'
| Triangle'
data Shape :: Shape' -> * where
Circle :: { radius :: Int} -> Shape Circle'
Square :: { side :: Int} -> Shape Square'
Triangle
:: { a :: Int
, b :: Int
, c :: Int}
-> Shape Triangle'
test1 :: Shape Circle' -> Int
test1 = undefined
但是,如果我想采用圆形或正方形的形状怎么办?如果我还想采用所有形状来实现单独的功能怎么办?
我是否可以定义一组要使用的Shape'
种类型,还是可以对每个数据允许多个datakind定义?
编辑:
使用工会似乎无效:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
...
type family Union (a :: [k]) (r :: k) :: Constraint where
Union (x ': xs) x = ()
Union (x ': xs) y = Union xs y
data Shape'
= Circle'
| Square'
| Triangle'
data Shape :: Shape' -> * where
Circle :: { radius :: Int} -> Shape Circle'
Square :: { side :: Int} -> Shape Square'
Triangle
:: { a :: Int
, b :: Int
, c :: Int}
-> Shape Triangle'
test1 :: Union [Circle', Triangle'] s => Shape s -> Int
test1 Circle {} = undefined
test1 Triangle {} = undefined
test1 Square {} = undefined
上面的部分编译
答案 0 :(得分:6)
这有点可怕,但是我想您可能需要使用Data.Type.Equality
来证明它是圆形还是正方形:
test1 :: Either (s :~: Circle') (s :~: Square') -> Shape s -> Int
现在,用户必须给出一个额外的参数(“证明词”),说明它是哪个。
实际上,您可以通过以下方式使用证明术语的想法来“完善”布拉德姆的解决方案:
class MyOpClass sh where
myOp :: Shape sh -> Int
shapeConstraint :: Either (sh :~: Circle') (sh :~: Square')
现在没有人可以添加更多实例(除非他们使用undefined
,这是不礼貌的。)
答案 1 :(得分:6)
(我认为)可以将类型族与ConstraintKinds
和PolyKinds
一起使用,以一种相当干净的方式完成这样的事情:
type family Union (a :: [k]) (r :: k) :: Constraint where
Union (x ': xs) x = ()
Union (x ': xs) y = Union xs y
test1 :: Union [Circle', Triangle'] s => Shape s -> Int
test1 = undefined
上面的()
是空约束(就像类型类约束的空“列表”一样)。
类型族的第一个“等式”利用类型族中可用的非线性模式匹配(它在左侧两次使用x
)。类型族还利用以下事实:如果所有情况都不匹配,则不会给您有效的约束。
您还应该能够使用类型级别的布尔值来代替ConstraintKinds
。那会比较麻烦,我认为最好避免在这里使用类型级别的布尔值(如果可以的话)。
旁注(我永远都记不清了,不得不为这个答案查询它):通过从Constraint
导入GHC.Exts
,可以看到Union
。
这里是对它的修改,以(部分)禁止不可达的定义以及无效的调用。环形交叉路口稍微多一点,但似乎可行。
修改*
以给出type family Union (a :: [k]) (r :: k) :: * where
Union (x ': xs) x = ()
Union (x ': xs) y = Union xs y
而不是约束,如下所示:
()
类型的大小无关紧要,只要它具有您可以对其进行模式匹配的居民,那么我就退还test1 :: Shape s -> Union [Circle', Triangle'] s -> Int
test1 Circle {} () = undefined
test1 Triangle {} () = undefined
-- test1 Square {} () = undefined -- This line won't compile
类型(单位类型)。
这是您将如何使用它:
x
如果忘记匹配它(例如,如果将变量名放在()
上而不是在Union
构造函数上匹配),则可能会定义一个无法到达的大小写。当您实际尝试达到这种情况时,它仍然会在调用站点上提供类型错误,但是(因此,即使您在test1 (Square undefined) ()
参数上不匹配,调用Union
也不会类型检查)。
请注意,似乎Shape
自变量必须位于public class CallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String msg = "Phone state changed to " + state;
if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
msg += ". Incoming number is " + incomingNumber;
// TODO This would be a good place to "Do something when the phone rings" ;-)
}
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
}
自变量之后才能正常工作(无论如何,都已完全如此)。
答案 2 :(得分:1)
您可以使用类型类:
class MyOpClass sh where
myOp :: Shape sh -> Int
instance MyOpClass Circle' where
myOp (Circle r) = _
instance MyOpClass Square' where
myOP (Square s) = _
对我来说,这并不是一个特别“完整”的解决方案-任何人都可以返回并添加另一个instance MyOpClass Triangle'
-但我想不出任何其他解决方案。通过不导出类型类,您可能可以避免此问题。
答案 3 :(得分:0)
我注意到的另一种解决方案虽然很冗长,但它是创建一种具有功能布尔值列表的类型。然后,您可以在限制类型时对特征进行模式匹配:
-- [circleOrSquare] [triangleOrSquare]
data Shape' =
Shape'' Bool
Bool
data Shape :: Shape' -> * where
Circle :: { radius :: Int} -> Shape (Shape'' True False)
Square :: { side :: Int} -> Shape (Shape'' True True)
Triangle
:: { a :: Int
, b :: Int
, c :: Int}
-> Shape (Shape'' False True)
test1 :: Shape (Shape'' True x) -> Int
test1 Circle {} = 2
test1 Square {} = 2
test1 Triangle {} = 2
在这里,三角形将不匹配:
• Couldn't match type ‘'True’ with ‘'False’
Inaccessible code in
a pattern with constructor:
Triangle :: Int -> Int -> Int -> Shape ('Shape'' 'False 'True),
in an equation for ‘test1’
• In the pattern: Triangle {}
In an equation for ‘test1’: test1 Triangle {} = 2
|
52 | test1 Triangle {} = 2
| ^^^^^^^^^^^
不幸的是,我认为您不能将此记录写为记录,这样可能会更清晰,并且避免了功能的排序。
这可能与类示例结合使用以提高可读性。