是否有可能对haskell类型变量设置不等式约束?

时间:2011-08-04 09:30:01

标签: haskell type-inference type-systems

是否有可能对函数的类型变量设置不等式约束,如GHC type family docs中的foo :: (a ~ b) => a -> b},除了不等式而不是等式?

我意识到可能没有直接的方法来做到这一点(因为ghc文档没有列出任何我的知识),但如果根据所有这些都不是某种可能的话,我会感到困惑。异乎寻常的类型 - 我已经接触过。

4 个答案:

答案 0 :(得分:26)

首先,请记住,不同的类型变量在其范围内已经是不可统一的 - 例如,如果你有\x y -> x,给它类型签名a -> b -> c会产生一个关于不存在的错误能够匹配刚性类型变量。因此,这仅适用于调用该函数的任何内容,从而阻止它以一种使两种类型相等的方式使用其他简单的多态函数。它会像这样工作,我假设:

const' :: (a ~/~ b) => a -> b -> a
const' x _ = x

foo :: Bool
foo = const' True False -- this would be a type error

我个人怀疑这会非常有用 - 类型变量的独立性已经阻止泛型函数崩溃到一些微不足道的事情,知道两种类型不相等实际上并没有让你做任何有趣的事情(不像平等,它让你强迫在这两种类型之间),这种事物是声明性而非条件性意味着你不能用它来区分等于/不等于某种专业化技术的一部分。

所以,如果你想要一些特定的用途,我建议尝试不同的方法。

另一方面,如果你只想玩荒谬的类型hackery ......

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverlappingInstances #-}

-- The following code is my own hacked modifications to Oleg's original TypeEq. Note
-- that his TypeCast class is no longer needed, being basically equivalent to ~.

data Yes = Yes deriving (Show)
data No = No deriving (Show)

class (TypeEq x y No) => (:/~) x y
instance (TypeEq x y No) => (:/~) x y

class (TypeEq' () x y b) => TypeEq x y b where
    typeEq :: x -> y -> b
    maybeCast :: x -> Maybe y

instance (TypeEq' () x y b) => TypeEq x y b where
    typeEq x y = typeEq' () x y
    maybeCast x = maybeCast' () x

class TypeEq' q x y b | q x y -> b where
    typeEq' :: q -> x -> y -> b
    maybeCast' :: q -> x -> Maybe y

instance (b ~ Yes) => TypeEq' () x x b where
    typeEq' () _ _ = Yes
    maybeCast' _ x = Just x

instance (b ~ No) => TypeEq' q x y b where
    typeEq' _ _ _ = No
    maybeCast' _ _ = Nothing

const' :: (a :/~ b) => a -> b -> a
const' x _ = x
嗯,这太令人难以置信了。但是工作:

> const' True ()
True
> const' True False

<interactive>:0:1:
    Couldn't match type `No' with `Yes'
    (...)

答案 1 :(得分:10)

来自GHC 7.8.1。 closed type families are available。解决方案更加简单:

data True
data False

type family TypeEqF a b where
  TypeEqF a a = True
  TypeEqF a b = False

type TypeNeq a b = TypeEqF a b ~ False

答案 2 :(得分:0)

改进Boldizsar的答案,这本身就是对已接受答案的改进:

{-# language DataKinds, TypeFamilies, TypeOperators, UndecidableInstances #-}

import Data.Kind (Constraint)
import GHC.TypeLits (TypeError, ErrorMessage(..))

data Foo = Foo

data Bar = Bar

notBar :: Neq Bar a => a -> ()
notBar _ = ()

type family Neq a b :: Constraint where
  Neq a a = TypeError
    ( 'Text "Expected a type that wasn't "
    ':<>: 'ShowType a
    ':<>: 'Text "!"
    )
  Neq _ _ = ()

*Main> notBar Foo
()
*Main> notBar Bar

<interactive>:12:1: error:
    • Expected a type that wasn't Bar!
    • In the expression: notBar Bar
      In an equation for ‘it’: it = notBar Bar

此类型错误很好,而且可读性强。

答案 3 :(得分:0)

现在,可以将Data.Type.Equality(或Singletons库)中的==与DataKinds扩展一起使用:

  foo :: (a == b) ~ 'False => a -> b