gpt4 book ai didi

haskell - Haskell 类型族中的类型歧义

转载 作者:行者123 更新时间:2023-12-04 23:41:29 25 4
gpt4 key购买 nike

我正在尝试将以下类(class)放在一起Domain及其实例TrivialDomain

{-# LANGUAGE TypeFamilies #-}

data Transition = Transition

class Domain d where
type Set d
type Engine d :: * -> *

top :: Engine d (Set d)

-- ...
complement :: Set d -> Engine d (Set d)
exclude :: Set d -> Set d -> Engine d (Set d)
-- ...

data TrivialDomain = TrivialDomain

instance Domain TrivialDomain where
type Set TrivialDomain = [Int]
type Engine TrivialDomain = IO

top = return [0..10]

-- ...
complement a = top >>= (flip exclude) a
exclude a b = return $ filter (not . (`elem` b)) a
-- ...

但我不断收到以下我无法理解的错误
test3.hs:25:21:
Couldn't match type ‘Engine d0’ with ‘IO’
The type variable ‘d0’ is ambiguous
Expected type: IO (Set d0)
Actual type: Engine d0 (Set d0)
In the first argument of ‘(>>=)’, namely ‘top’
In the expression: top >>= (flip exclude) a
test3.hs:25:35:
Couldn't match type ‘Set d1’ with ‘[Int]’
The type variable ‘d1’ is ambiguous
Expected type: Set d0 -> [Int] -> IO [Int]
Actual type: Set d1 -> Set d1 -> Engine d1 (Set d1)
In the first argument of ‘flip’, namely ‘exclude’
In the second argument of ‘(>>=)’, namely ‘(flip exclude) a’

我希望 Engine d (Set d)解析为 IO [Int]在实例声明中,似乎并非如此。至少 GHC 不这么认为。我错过了什么?

最佳答案

在您的情况下,关联类型不足以推断方法的类型。

你有课Domain d , 和 SetEngine关联到 d .这意味着只要有一个已知的 d在我们的程序中,已知 Domain d例如,GHC 可以解析 Set dEngine d .但这并不适用。 GHC 无法解析 dDomain来自 Set d 的实例或 Engine d , 因为完全有可能有不同的 Domain具有相同 Set 的实例和 Engine类型。

因为你的类方法只提到 SetEngine , Domain d永远不能从方法使用中推断出来。

你可以根据你的目标做几件事。

首先,您可以制作 d取决于 SetEngine :

class Domain set engine where
type DomainOf set engine :: *
-- ...

更一般地说, FunctionalDependencies 为您提供更大的灵 active 来强制类型之间的依赖关系。比如可以明确声明只有一个 d对于每个 Set ,这足以恢复良好的类型推断:
class Domain d set engine | d -> set engine, set -> d where

top :: engine set
complement :: set -> engine set
exclude :: set -> set -> engine set

data TrivialDomain = TrivialDomain

instance Domain TrivialDomain [Int] IO where

top = return [0..10]

complement a = top >>= (flip exclude) a

exclude a b = return $ filter (not . (`elem` b)) a

最后,如果你想使用你原来的类,你必须添加 Proxy d方法的参数,以使实例和关联的类型可解析:
import Data.Proxy

data Transition = Transition

class Domain d where
type Set d
type Engine d :: * -> *

top :: Proxy d -> Engine d (Set d)
complement :: Proxy d -> Set d -> Engine d (Set d)
exclude :: Proxy d -> Set d -> Set d -> Engine d (Set d)

data TrivialDomain = TrivialDomain

instance Domain TrivialDomain where
type Set TrivialDomain = [Int]
type Engine TrivialDomain = IO

top _ = return [0..10]

complement d a = top d >>= (flip (exclude d)) a
exclude d a b = return $ filter (not . (`elem` b)) a

在这里, Proxy d的目的|是准确指定您要使用的实例。

然而,这意味着我们必须写 top (Proxy :: Proxy d)在每种方法的使用上(与其他方法类似),这是相当繁重的。对于 GHC 8,我们可以省略 Proxy s 并使用 TypeApplications 反而:
{-# language TypeApplications, TypeFamilies #-}

-- ...

instance Domain TrivialDomain where
type Set TrivialDomain = [Int]
type Engine TrivialDomain = IO

top = return [0..10]

complement a = top @TrivialDomain >>= (flip (exclude @TrivialDomain)) a
exclude a b = return $ filter (not . (`elem` b)) a

关于haskell - Haskell 类型族中的类型歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36175839/

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