gpt4 book ai didi

haskell - ConstraintKinds 解释了一个 super 简单的例子

转载 作者:行者123 更新时间:2023-12-03 11:36:32 24 4
gpt4 key购买 nike

什么是Constraint kind ?

为什么有人会使用它(在实践中)?

到底有什么好处呢?

能否举一个简单的代码示例来说明前两个问题的答案?

为什么用在this例如代码?

最佳答案

好吧,我会提到它允许你做的两件实际的事情:

  • 通过类型类约束参数化类型
  • 编写允许其实例指定所需约束的类型类。

  • 也许最好用一个例子来说明这一点。经典的 Haskell 疣之一是您无法制作 Functor对类型参数施加类约束的类型的实例;例如, Set containers 中的类库,需要 Ord对其元素的约束。原因是在“ Vanilla ”Haskell 中,您必须对类本身进行约束:
    class OrdFunctor f where
    fmap :: Ord b => (a -> b) -> f a -> f b

    ...但是这个类只适用于需要一个 Ord 的类型。约束。不是通用解决方案!

    那么,如果我们可以采用该类定义并将 Ord 抽象出来呢?约束,允许个别实例说出他们需要什么约束?好吧, ConstraintKindsTypeFamilies允许:
    {-# 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

    (请注意,这不是将 Functor 实例设为 Set 的唯一障碍;参见 this discussion 。另外, credit to this answer for the NoConstraint trick 。)

    不过,这种解决方案还没有被普遍采用,因为 ConstraintKinds或多或少仍然是一个新功能。
    ConstraintKinds 的另一种用法是通过类约束或类对类型进行参数化。我会复制 this Haskell "Shape Example" code that I wrote :
    {-# LANGUAGE GADTs, ConstraintKinds, KindSignatures, DeriveDataTypeable #-}
    {-# LANGUAGE TypeOperators, ScopedTypeVariables, FlexibleInstances #-}

    module Shape where

    import Control.Applicative ((<$>), (<|>))
    import Data.Maybe (mapMaybe)
    import Data.Typeable
    import GHC.Exts (Constraint)

    -- | Generic, reflective, heterogeneous container for instances
    -- of a type class.
    data Object (constraint :: * -> Constraint) where
    Obj :: (Typeable a, constraint a) => a -> Object constraint
    deriving Typeable

    -- | Downcast an 'Object' to any type that satisfies the relevant
    -- constraints.
    downcast :: forall a constraint. (Typeable a, constraint a) =>
    Object constraint -> Maybe a
    downcast (Obj (value :: b)) =
    case eqT :: Maybe (a :~: b) of
    Just Refl -> Just value
    Nothing -> Nothing

    这里是 Object的参数type 是一个类型类(种类 * -> Constraint ),所以你可以有像 Object Shape 这样的类型在哪里 Shape是一个类:
    class Shape shape where
    getArea :: shape -> Double

    -- Note how the 'Object' type is parametrized by 'Shape', a class
    -- constraint. That's the sort of thing ConstraintKinds enables.
    instance Shape (Object Shape) where
    getArea (Obj o) = getArea o

    什么 Object类型确实是两个特征的组合:
  • 存在类型(此处由 GADTs 启用),它允许我们将异构类型的值存储在同一个 Object 中类型。
  • ConstraintKinds ,这使我们能够,而不是硬编码 Object对于某些特定的类约束集,拥有 Object 的用户类型指定他们想要的约束作为参数到Object类型。

  • 现在有了它,我们不仅可以制作 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)]

    ...但感谢 Typeable Object 中的约束我们可以 垂头丧气 : 如果我们猜对了 Object 中包含的类型,我们可以恢复原来的类型:
    -- | 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)

    关于haskell - ConstraintKinds 解释了一个 super 简单的例子,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31317159/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com