是否可以注释函数的特殊属性(例如,主观性)?

时间:2012-11-08 01:51:58

标签: haskell types higher-order-functions

假设我有一个更高阶函数f :: (a -> b) -> (a -> b)。但是f只有在输入函数是满射的情况下才能正常运行。反正有没有迫使这种情况发生在Haskell?例如,我真的希望f的类型签名类似于:

f :: (Surjective (a -> b)) => (a -> b) -> (a -> b)

但这不起作用,因为我不希望声明a -> b类型的所有函数都是满射的,只是其中的一部分。例如,也许f将一个反射函数转换为一个非满射函数。

我们可以将函数包装在特殊数据类型data Surjective f = Surjective f中,并定义

f :: Surjective (a -> b) -> (a -> b)

但这会使很难为函数分配多个属性。

在练习中有没有方便的方法呢?这在理论上是否可行?

3 个答案:

答案 0 :(得分:5)

这是一个如何在Agda中表达客观性的例子:

module Surjectivity where

open import Data.Product using ( ∃; ,_ )
open import Relation.Binary.PropositionalEquality using ( _≡_; refl )

Surjective : ∀ {a b} {A : Set a} {B : Set b} → (A → B) → Set _
Surjective {A = A} {B = B} f = ∀ (y : B) → ∃ λ (x : A) → f x ≡ y

例如,id是主观的(一个简单的证据):

open import Function using ( id )

id-is-surjective : ∀ {a} {A : Set a} → Surjective {A = A} id
id-is-surjective _ = , refl

采用另一种仅适用于射影函数的身份函数:

id-for-surjective's : ∀ {a b} {A : Set a} {B : Set b} → (F : A → B) → {proof : Surjective F} → A → B
id-for-surjective's f = f

我们可以将id传递给id-for-surjective's,并将其作为见证的证据证明:

id′ : ∀ {a} {A : Set a} → A → A
id′ = id-for-surjective's id {proof = id-is-surjective}

因此id′id的功能相同:

id≡id′ : ∀ {a} {A : Set a} → id {A = A} ≡ id′
id≡id′ = refl

尝试将非投射函数传递给id-for-surjective's是不可能的:

open import Data.Nat using ( ℕ )

f : ℕ → ℕ
f x = 1

f′ : ℕ → ℕ
f′ = id-for-surjective's f {proof = {!!}} -- (y : ℕ) → ∃ (λ x → f x ≡ y) (unprovable)

类似地,许多其他属性可以用这种方式表示,Agda的标准库已经有必要的定义(例如Function.SurjectionFunction.InjectionFunction.Bijection和其他模块。)

答案 1 :(得分:3)

首先,您通常使用newtype声明而不是data声明。 GHC使用newtypes进行类型检查,但在编译期间有效地擦除它们,因此生成的代码更有效。

使用newtype作为这种注释是Haskell中的一种常见解决方案,尽管正如您所指出的那样,如果需要围绕值包装许多属性,它并不完全令人满意。

您可以将newtype与类型组合在一起。在您想要的任何Surjective包装器上声明newtype类型类的类型类实例,并在f等函数中与该类型类匹配,而不是要求特定的{{1}包装器。

当然,更好的是能够让编译器检查函数是否真的是满足的......但这是一个开放的研究问题。 : - )

答案 2 :(得分:3)

如果类型依赖于值,我们可以在Haskell中类似依赖类型 这是由其类型唯一确定的。当然,这不是依赖类型 但它有时很有用。

所以让我们在类型层面构建一种小的建设性集合理论。 每种类型都代表一个特定的功能,并将由一个值居住 (不包括所有底层事物)。

将F定义为满足以下条件的最小集合:

  • id:: a -> a在F。
  • term:: a -> ()在F。
  • init:: Empty -> a在F中(其中Empty代表空集)。
  • p1 :: (a,b) -> a在F。
  • i1 :: a -> Either a b在F。
  • flip :: (a,b) -> (b,a)在F。
  • 如果f::a -> bg::b -> c都在F中,则g.f :: a -> c在F中。
  • 如果f::a -> bg::c -> d都在F中,那么 以下功能在F:

      f*g :: (a,c) -> (b,d)
      f*g (x,y) = (f x,g y)
      f + g :: Either a b -> Either c d
      (f+g) (Left x) = f x
      (f+g) (Right y) = g y`
  • (此处添加更好的归纳规则,以便您最喜欢的功能可以包含在F中。)

集合F用于表示在Haskell中类型级别可编码的函数, 同时,它的各种性质如外在性,注入性等都可以证明 Haskell中的类型级函数。

在关联类型的帮助下,我们可以干净地编码F,如下所示:

class Function f where
    type Dom f :: *
    type Codom f :: *
    apply :: f -> Dom f -> Codom f

data ID a = ID  -- represents id :: a -> a
instance Function (ID a) where
    type Dom (ID a) = a
    type Codom (ID a) = a
    apply _ x = x

data P1 a b = P1 -- represents the projection (a,b) -> a
instance Function (P1 a b) where
    type Dom (P1 a b) = (a,b)
    type Codom (P1 a b) = a
    apply _ (x,y) = x

...

data f :.: g = f :.: g  -- represents the composition (f.g)
instance ( Function f
         , Function g
         , Dom f ~ Codom g)
         => Function (f :.: g) where
     type Dom (f :.: g) = Dom g
     type Codom (f :.: g) = Codom f
     apply (f :.: g) x = apply f (apply g x)
 ...

类型级谓词“f is surjective”可以表示为类实例:

class Surjective f where
instance Surjective (ID a)  where
instance Surjective (P1 a b)  where
instance (Surjective f,Surjective g)
     => Surjection (f :.: g) where
 ..

最后,可以定义一个更高阶函数,它采用那些可证明的射影函数:

surjTrans :: (Function fun,Surjective fun)
             => fun -> Dom fun -> Codom fun
surjTrans surj x = apply surj x

同样适用于注射,同构等。 例如,可以声明只接受(建设性的)同构作为参数的高阶函数:

isoTrans :: (Function fun,Surjective fun,Injective fun)
            => fun -> Dom fun -> Codom fun
isoTrans iso x = apply iso x

如果转换采用更有趣的形式,那么它必须是一个类方法并由结构定义 每个函数的递归(由其类型唯一确定)。

我当然不是逻辑或Haskell的专家,我真的想知道这个理论有多么强大。 如果您发现了这个,可以发布更新吗?

以下是完整代码:


{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}

infixl 6 :.:
infixl 5 :*:
infixl 4 :+:

data TRUE
data Empty

class Function f where
  type Dom f :: *
  type Codom f :: *
  apply :: f -> Dom f -> Codom f

instance Function (a -> b) where
  type Dom (a->b) = a
  type Codom (a->b) = b
  apply f x = f x

data ID a = ID
data Initial  a = Initial
data Terminal a = Terminal
data P1 a b = P1
data P2 a b = P2
data I1 a b = I1
data I2 a b = I2
data FLIP a b = FLIP
data COFLIP a b = COFLIP
data f :.: g = f :.: g
data f :*: g = f :*: g
data f :+: g = f :+: g

instance Function (ID a) where
  type Dom (ID a) = a
  type Codom (ID a) = a
  apply _ x = x

instance Function (Initial a) where
  type Dom (Initial a) = Empty
  type Codom (Initial a) = a
  apply _ _ = undefined

instance Function (Terminal a) where
  type Dom (Terminal a) = a
  type Codom (Terminal a) = ()
  apply _ _ = ()

instance Function (P1 a b) where
  type Dom (P1 a b) = (a,b)
  type Codom (P1 a b) = a
  apply _ (x,y) = x

instance Function (P2 a b) where
  type Dom (P2 a b) = (a,b)
  type Codom (P2 a b) = b
  apply _ (x,y) = y

instance Function (I1 a b) where
  type Dom (I1 a b) = a
  type Codom (I1 a b) = Either a b
  apply _ x = Left x

instance Function (I2 a b) where
  type Dom (I2 a b) = b
  type Codom (I2 a b) = Either a b
  apply _ y = Right y

instance Function (FLIP a b) where
  type Dom (FLIP a b) = (a,b)
  type Codom (FLIP a b) = (b,a)
  apply _ (x,y) = (y,x)

instance Function (COFLIP a b)  where
  type Dom (COFLIP a b) = Either a b
  type Codom (COFLIP a b) = Either b a
  apply _ (Left x) = Right x
  apply _ (Right y) = Left y

instance ( Function f
         , Function g
         , Dom f ~ Codom g)
         => Function (f :.: g) where
  type Dom (f :.: g) = Dom g
  type Codom (f :.: g) = Codom f
  apply (f :.: g) x = apply f (apply g x)

instance (Function f, Function g)
         => Function (f :*: g)  where
  type Dom (f :*: g) = (Dom f,Dom g)
  type Codom (f :*: g) = (Codom f,Codom g)
  apply (f :*: g) (x,y) = (apply f x,apply g y)

instance (Function f, Function g)
         => Function (f :+: g) where
  type Dom (f :+: g) = Either (Dom f) (Dom g)
  type Codom (f :+: g) = Either (Codom f) (Codom g)
  apply (f :+: g) (Left x)  = Left (apply f x)
  apply (f :+: g) (Right y) = Right (apply g y)



class Surjective f where
class Injective f  where
class Isomorphism f where

instance Surjective (ID a)  where
instance Surjective (Terminal a)  where
instance Surjective (P1 a b)  where
instance Surjective (P2 a b)  where
instance Surjective (FLIP a b)  where
instance Surjective (COFLIP a b) where
instance (Surjective f,Surjective g)
         => Surjective (f :.: g) where
instance (Surjective f ,Surjective g )
         => Surjective (f :*: g)  where
instance (Surjective f,Surjective g )
         => Surjective (f :+: g)  where

instance Injective (ID a)  where
instance Injective (Initial a)  where
instance Injective (I1 a b)  where
instance Injective (I2 a b)  where
instance Injective (FLIP a b)  where
instance Injective (COFLIP a b)  where
instance (Injective f,Injective g)
         => Injective (f :.: g) where
instance (Injective f ,Injective g )
         => Injective (f :*: g)  where
instance (Injective f,Injective g )
         => Injective (f :+: g)  where

instance (Surjective f,Injective f)
         => Isomorphism f  where


surjTrans :: (Function fun,Surjective fun)
             => fun -> Dom fun -> Codom fun
surjTrans surj x = apply surj x

injTrans :: (Function fun,Injective fun)
            => fun -> Dom fun -> Codom fun
injTrans inj x = apply inj x

isoTrans :: (Function fun,Isomorphism fun)
            => fun -> Dom fun -> Codom fun
isoTrans iso x = apply iso x


g1 :: FLIP a b
g1 = FLIP

g2 :: FLIP a b :*: P1 c d
g2 = FLIP :*: P1

g3 :: FLIP a b :*: P1 c d :.: P2 e (c,d)
g3 = FLIP :*: P1 :.: P2

i1 :: I1 a b
i1 = I1

例如,以下是一些输出(请参阅Haskell在类型检查时如何证明'那些递归属性):


isoTrans  g1 (1,2)
==> (2,1)
surjTrans g2 ((1,2),(3,4))
==> ((2,1),3)
injTrans  g2 ((1,2),(3,4))
==>     No instance for (Injective (P1 c0 d0)) ..

surjTrans i1 1 :: Either Int Int
==>     No instance for (Surjective (I1 Int Int)) ..
injTrans i1 1 :: Either Int Int
==>  Left 1
isoTrans i1 1 :: Either Int Int
==>      No instance for (Surjective (I1 Int Int)) ..