为什么扩展我的函数依赖会消除非类型变量参数?

Sri*_*aic 2 haskell ghc functional-dependencies

我一直在做一些构建我自己的自定义前奏的工作,我想构建一个Callable类型类,它将($)为函数以外的类型实现函数application().所以我使用多参数类型类构建了一个类型类:

{-# Language MultiParamTypeClasses #-}

import Prelude ()

class Callable a b c where
  ($) :: a -> b -> c
Run Code Online (Sandbox Code Playgroud)

现在我继续将函数作为Callable类型类的实例,这需要我启用灵活的实例.

{-# Language MultiParamTypeClasses, FlexibleInstances #-}

import Prelude ()

id :: a -> a
id x = x

class Callable a b c where
  ($) :: a -> b -> c

instance Callable (a -> b) a b where
  ($) = id
Run Code Online (Sandbox Code Playgroud)

这很好,现在我可以($)在函数上使用.因此,对我来说,下一个合乎逻辑的步骤是实现函数组合((.)).经过一段时间的努力,我意识到为了做到这一点,我需要在Callable功能上依赖,所以我打开了功能依赖.

{-# Language MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}

import Prelude ()

id :: a -> a
id x = x

class Callable a b c | a b -> c where
  ($) :: a -> b -> c

instance Callable (a -> b) a b where
  ($) = id

(.) :: (Callable f1 intype intermediate, Callable f2 intermediate outtype) => f2 -> f1 -> intype -> outtype
(.) a b c = a $ (b $ c)
Run Code Online (Sandbox Code Playgroud)

这实际上编译正常.事实上,如果我可以使用我的(.)功能.但是,如果我尝试使用我的新功能(至少以我尝试过的任何方式),它就不会出现相当神秘的错误.

 ~/p/dynamo > ghci callable.hs
GHCi, version 8.4.2: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( callable.hs, interpreted )
Ok, one module loaded.
*Main> :t (id).(id)
(id).(id)
  :: (Callable (a1 -> a1) c e, Callable (a2 -> a2) e d) => c -> d
*Main> ((id).(id)) $ ()

<interactive>:2:1: error:
    • Non type-variable argument
        in the constraint: Callable (c1 -> d) () c2
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall c1 d c2 a1 e a2.
              (Callable (c1 -> d) () c2, Callable (a1 -> a1) c1 e,
               Callable (a2 -> a2) e d) =>
              c2
*Main>
Run Code Online (Sandbox Code Playgroud)

我真的无法理解这个错误试图传达的内容.但它表明我打开灵活的上下文所以我认为我会给它一个旋转,如果它解决了这个问题很好,如果它改变了错误我可能会遇到问题.但是,如果我打开灵活的上下文,错误不会改变,事实上它甚至仍然暗示我打开灵活的上下文.

此时我以为我会做一些阅读.我读了几个问题Non type-variable argument,但我并不觉得我对我的特定问题有任何了解.正是在这一点上,我脑子里的某些东西让我想到了b一个功能依赖.我不知道为什么,但实际上这确实解决了我的问题.以下是工作代码的样子:

{-# Language MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}

import Prelude ()

id :: a -> a
id x = x

class Callable a b c | a -> b c where
  ($) :: a -> b -> c

instance Callable (a -> b) a b where
  ($) = id

(.) :: (Callable f1 intype intermediate, Callable f2 intermediate outtype) => f2 -> f1 -> intype -> outtype
(.) a b c = a $ (b $ c)
Run Code Online (Sandbox Code Playgroud)

所以我的问题当然是为什么这样做有效?我做错了什么,改变是如何解决的?

Jon*_*rdy 6

使用fundep a b -> c,你会说函数type(a)和参数type(b)确定结果类型(c).更改此值a -> b c意味着函数类型确定参数和结果类型,这是您想要的:如果a替换为a' -> b',bwith a'cwith b',那么函数类型确实包含消除歧义所需的信息.