haskell - 应用实例化中的刚性变量

标签 haskell types type-mismatch type-variables

我在安全性的应用实例化中遇到编译器错误,如下所示:

{-# LANGUAGE InstanceSigs #-}

module Main where

main :: IO ()
main = pure ()


data Safeness a 
    = Unsafe a
    | Safe a
    | VerySafe a
    deriving (Eq, Show)


instance Functor Safeness where
    fmap :: (a -> b) -> Safeness a -> Safeness b
    fmap f (Unsafe x) = Unsafe $ f x
    fmap f (Safe x) = Safe $ f x
    fmap f (VerySafe x) = VerySafe $ f x


instance Applicative Safeness where
    pure :: a -> Safeness a
    pure x = Safe x

    (<*>) :: Safeness (a -> b) -> Safeness a -> Safeness b
    (<*>) (Safe f) (Safe x) = Safe $ f x       
    (<*>) (VerySafe f) (VerySafe x) = VerySafe $ f x    
    (<*>) _ x = x     

编译器错误:

    * Couldn't match type `a' with `b'
      `a' is a rigid type variable bound by
        the type signature for:
          (<*>) :: forall a b. Safeness (a -> b) -> Safeness a -> Safeness b      
        at C:\practise\app\Main.hs:27:14-58       
      `b' is a rigid type variable bound by
        the type signature for:
          (<*>) :: forall a b. Safeness (a -> b) -> Safeness a -> Safeness b      
        at C:\practise\app\Main.hs:27:14-58       
      Expected type: Safeness b
        Actual type: Safeness a
    * In the expression: x
      In an equation for `<*>': (<*>) _ x = x
      In the instance declaration for `Applicative Safeness'
    * Relevant bindings include
        x :: Safeness a
          (bound at C:\practise\app\Main.hs:30:13)
        (<*>) :: Safeness (a -> b) -> Safeness a -> Safeness b
          (bound at C:\practise\app\Main.hs:28:5) 
   |
30 |     (<*>) _ x = x       
   |   

我知道 b 不必与 a 相同,但可以;我总是强制它在某种情况下保持相同。这有什么问题,我还能如何编码以获得预期的功能?

最佳答案

正如错误消息所示,问题是这样的:

(<*>) _ x = x

为了清楚起见,让我们给第一个参数命名,并为主体提供一个“洞”:

(<*>) f x = _

然后编译器会告诉我们:

/Users/jpurdy/Code/Safe.hs:24:17: error:
    • Found hole: _ :: Safeness b
    …
    • Relevant bindings include
        x :: Safeness a
        f :: Safeness (a -> b)
        (<*>) :: Safeness (a -> b) -> Safeness a -> Safeness b

您可以返回的唯一值是 Unsafe (_::b)Safe (_::b)VerySafe (_::b) ),因此您必须提供 b;根据参数化,获得 b唯一方法是通过匹配 f 来获得 a -> b > 并匹配 x 以获得 a,并将函数应用于该值。换句话说,您不能仅返回未修改的 x

不幸的是,解决方案取决于您想要做什么。您可以使用两个参数的最低安全性来标记结果:

VerySafe f <*> VerySafe x = VerySafe $ f x
VerySafe f <*> Safe x     = Safe $ f x
VerySafe f <*> Unsafe x   = Unsafe $ f x

Safe f     <*> VerySafe x = Safe $ f x
Safe f     <*> Safe x     = Safe $ f x
Safe f     <*> Unsafe x   = Unsafe $ f x

Unsafe f   <*> VerySafe x = Unsafe $ f x
Unsafe f   <*> Safe x     = Unsafe $ f x
Unsafe f   <*> Unsafe x   = Unsafe $ f x

通过从类型中分解安全参数(在类型代数中,从 a + a + a3 × a),这会更简单:

data Safety = Unsafe | Safe | VerySafe
  deriving (Eq, Ord)

data Safeness a = Safeness Safety a

instance Applicative Safeness where

  -- Combine safeties with ‘min’, or use
  -- ‘deriving (Semigroup, Monoid) via (Min Safety)’
  -- to use the Semigroup ‘<>’ operator.
  Safeness s1 f <*> Safeness s2 x
    = Safeness (min s1 s2) (f x)

  -- Must use ‘VerySafe’ instead of ‘Safe’
  -- since it’s the identity for ‘min’,
  -- to satisfy the ‘Applicative’ laws.
  -- (Or respectively ‘mempty’ = ‘maxBound’.)
  pure = Safeness VerySafe

或者您可以使用 GADT 将安全参数提升到类型级别,放弃 Applicative 实例,但只允许具有相同安全性的值合并:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}

data Safety = U | S | V

data Safeness (s :: Safety) a where
  VerySafe :: a -> Safeness 'V a
  Safe     :: a -> Safeness 'S a
  Unsafe   :: a -> Safeness 'U a

apply :: Safeness s (a -> b) -> Safeness s a -> Safeness s b
apply (VerySafe f) (VerySafe x) = VerySafe (f x)
apply (Safe f)     (Safe x)     = Safe (f x)
apply (Unsafe f)   (Unsafe x)   = Unsafe (f x)
-- (Other cases are impossible because ‘s’ won’t match.)

顺便说一句,您可以使用DeriveFunctor扩展来编写deriving (Functor)以实现安全

关于haskell - 应用实例化中的刚性变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63252672/

相关文章:

Haskell:不在范围内:foldl'?

python - "Can' t convert 'int' object to str implicitly”错误(Python)

typescript - 为什么这段代码没有给我一个带有 --noImplicitAny 的错误?

spring - TypeMismatchException 提供的 ID 类型错误

c# - C# 数组中的协方差被破坏?

haskell - 具有零构造函数的代数数据类型有什么意义?

haskell - 懒惰和纯洁有什么关系?

haskell - 如何向秒差距中的给定位置发出失败消息

c++ - 使用 ifstream 检查 C++ 中的数据类型

kotlin - 在 Kotlin 中, "::class.simpleName"是做什么的?