Haskell ConstraintKinds在一个超级简单的示例中进行了解释
什么是a 为什么会有人(在实践中)使用它 它有什么好处 您能给出一个简单的代码示例来说明前两个问题的答案吗Haskell ConstraintKinds在一个超级简单的示例中进行了解释,haskell,ghc,type-kinds,constraint-kinds,Haskell,Ghc,Type Kinds,Constraint Kinds,什么是a 为什么会有人(在实践中)使用它 它有什么好处 您能给出一个简单的代码示例来说明前两个问题的答案吗 例如,为什么在代码中使用它?好吧,我将提到它允许您做的两件实际事情: 通过类型类约束参数化类型 编写允许其实例指定所需约束的类型类 也许最好用一个例子来说明这一点。Haskell的一个经典缺点是,不能为对其类型参数施加类约束的类型创建Functor实例;例如,containers库中的Set类,需要对其元素进行Ord约束。原因是在“香草”Haskell中,必须对类本身进行约束: class
例如,为什么在代码中使用它?好吧,我将提到它允许您做的两件实际事情:
Functor
实例;例如,containers
库中的Set
类,需要对其元素进行Ord
约束。原因是在“香草”Haskell中,必须对类本身进行约束:
class OrdFunctor f where
fmap :: Ord b => (a -> b) -> f a -> f b
…但该类仅适用于特别需要Ord
约束的类型。不是一个普遍的解决办法
那么,如果我们可以使用该类定义并抽象掉Ord
约束,允许单个实例说出它们需要什么约束,会怎么样?嗯,ConstraintKinds
plusTypeFamilies
允许:
{-# LANGUAGE ConstraintKinds, TypeFamilies, FlexibleInstances #-}
import Prelude hiding (Functor(..))
import GHC.Exts (Constraint)
import Data.Set (Set)
import qualified Data.Set as Set
-- | A 'Functor' over types that satisfy some constraint.
class Functor f where
-- | The constraint on the allowed element types. Each
-- instance gets to choose for itself what this is.
type Allowed f :: * -> Constraint
fmap :: Allowed f b => (a -> b) -> f a -> f b
instance Functor Set where
-- | 'Set' gets to pick 'Ord' as the constraint.
type Allowed Set = Ord
fmap = Set.map
instance Functor [] where
-- | And `[]` can pick a different constraint than `Set` does.
type Allowed [] = NoConstraint
fmap = map
-- | A dummy class that means "no constraint."
class NoConstraint a where
-- | All types are trivially instances of 'NoConstraint'.
instance NoConstraint a where
(请注意,这不是将函子
实例设置为集
的唯一障碍;另请参见。)
不过,这种解决方案还没有被普遍采用,因为ConstraintKinds
或多或少还是一种新特性
ConstraintKinds
的另一个用途是通过类约束或类参数化类型。我将复制:
对象
类型所做的是两个功能的组合:
GADTs
启用),它允许我们在同一对象
类型中存储异构类型的值ConstraintKinds
,它允许我们将对象
硬编码到某些特定的类约束集,而不是让对象
类型的用户指定他们想要作为对象
类型参数的约束Shape
实例的异构列表:
data Circle = Circle { radius :: Double }
deriving Typeable
instance Shape Circle where
getArea (Circle radius) = pi * radius^2
data Rectangle = Rectangle { height :: Double, width :: Double }
deriving Typeable
instance Shape Rectangle where
getArea (Rectangle height width) = height * width
exampleData :: [Object Shape]
exampleData = [Obj (Circle 1.5), Obj (Rectangle 2 3)]
…但由于对象
中的可键入
约束,我们可以向下转换:如果我们正确猜测对象
中包含的类型,我们可以恢复原始类型:
-- | For each 'Shape' in the list, try to cast it to a Circle. If we
-- succeed, then pass the result to a monomorphic function that
-- demands a 'Circle'. Evaluates to:
--
-- >>> example
-- ["A Circle of radius 1.5","A Shape with area 6.0"]
example :: [String]
example = mapMaybe step exampleData
where step shape = describeCircle <$> (downcast shape)
<|> Just (describeShape shape)
describeCircle :: Circle -> String
describeCircle (Circle radius) = "A Circle of radius " ++ show radius
describeShape :: Shape a => a -> String
describeShape shape = "A Shape with area " ++ show (getArea shape)
——|对于列表中的每个“形状”,尝试将其转换为圆形。如果我们
--成功,然后将结果传递给
--需要一个“圆圈”。评估结果为:
--
-->>>示例
--[“半径为1.5的圆”,“面积为6.0”的形状]
示例::[String]
示例=映射可能步骤示例数据
其中阶梯形状=描述圆形(向下浇铸形状)
只是(描述形状)
描述圆::圆->字符串
描述圆(圆半径)=“半径的圆”++显示半径
描述形状::形状a=>a->字符串
describeShape shape=“带区域的形状”++显示(getArea shape)
data Circle = Circle { radius :: Double }
deriving Typeable
instance Shape Circle where
getArea (Circle radius) = pi * radius^2
data Rectangle = Rectangle { height :: Double, width :: Double }
deriving Typeable
instance Shape Rectangle where
getArea (Rectangle height width) = height * width
exampleData :: [Object Shape]
exampleData = [Obj (Circle 1.5), Obj (Rectangle 2 3)]
-- | For each 'Shape' in the list, try to cast it to a Circle. If we
-- succeed, then pass the result to a monomorphic function that
-- demands a 'Circle'. Evaluates to:
--
-- >>> example
-- ["A Circle of radius 1.5","A Shape with area 6.0"]
example :: [String]
example = mapMaybe step exampleData
where step shape = describeCircle <$> (downcast shape)
<|> Just (describeShape shape)
describeCircle :: Circle -> String
describeCircle (Circle radius) = "A Circle of radius " ++ show radius
describeShape :: Shape a => a -> String
describeShape shape = "A Shape with area " ++ show (getArea shape)