我想基于元组创建递归实例类型。我正在寻找的是类似的东西:
class Provider a b where
getInstance :: a -> b
instance Provider a b => Provider (x, a) b where
getInstance (x, a) = getInstance a
instance Provider (b, x) b where
getInstance (b, _) = b
tryFunc1 :: Int
tryFunc1 =
let provider = ("test", (10, ())) :: (String, (Int, ()))
in getInstance provider
tryFunc2 :: String
tryFunc2 =
let provider = ("test", (10, ())) :: (String, (Int, ()))
in getInstance provider
不幸的是,haskell未能解决该实例。有什么原因吗?
答案 0 :(得分:5)
解决方案是停止使用已弃用的OverlappingInstances
pragma并开始使用每个实例OVERLAPPING
和OVERLAPPABLE
pragma。只有这个改变:
instance {-# OVERLAPPABLE #-} Provider a b => Provider (x, a) b where
getInstance (x, a) = getInstance a
instance {-# OVERLAPPING #-} Provider (b, x) b where
getInstance (b, _) = b
我认为tryFunc1
为10
而tryFunc2
为"test"
。
从技术上讲,你只需要OVERLAPPABLE
或OVERLAPPING
编译指示,但我相信在这种情况下同时使用两者都是好习惯...而且,我认为这是你想要的行为,但请注意,这只是获取您要查找的任何类型的第一个(因此getInstance (10, (20, ())) :: Int
为我提供10
而不是 {{1} })
良好的信息来源是ticket跟踪功能的创建。
答案 1 :(得分:1)
我知道有些人不喜欢UndecidableInstances
但这就是我喜欢在这种情况下做的事情:使用一个封闭的type family
来明确选择是完全确定的。
我们的想法是让类型族计算一个布尔标志,以便清楚类型类解析机制应该采用哪个分支。由于UndecidableInstance
约束,Provider a b (AtHead a b) =>
扩展名是必需的,即使它是无害的。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Provider where
import Data.Proxy
import Data.Type.Equality
class Provider a b (f :: Bool) where
getInstance' :: Proxy f -> a -> b
type family AtHead x y :: Bool where
AtHead (x, a) y = x == y
instance Provider a b (AtHead a b) => Provider (x, a) b 'False where
getInstance' _ (x, a) = getInstance' (Proxy :: Proxy (AtHead a b)) a
instance Provider (b, x) b 'True where
getInstance' _ (b, _) = b
getInstance :: forall a b. Provider a b (AtHead a b) => a -> b
getInstance = getInstance' (Proxy :: Proxy (AtHead a b))
tryFunc1 :: Int
tryFunc1 =
let provider = ("test", (10, ())) :: (String, (Int, ()))
in getInstance provider
tryFunc2 :: String
tryFunc2 =
let provider = ("test", (10, ())) :: (String, (Int, ()))
in getInstance provider