[英]Haskell Recursive Type Classes
I want to create a recursive instance type based on tuples.我想创建一个基于元组的递归实例类型。 What I am looking for is something similar than this:
我正在寻找的是与此类似的东西:
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
Unfortunatelly, haskell fails to solve the instance.不幸的是,haskell 无法解决该实例。 Any reason?
任何原因?
The solution is to stop using the deprecated OverlappingInstances
pragma and start using the per instance OVERLAPPING
and OVERLAPPABLE
pragmas.解决方案是停止使用已弃用的
OverlappingInstances
编译指示,并开始使用每个实例的OVERLAPPING
和OVERLAPPABLE
编译指示。 With just this change:仅此更改:
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
I get tryFunc1
to be 10
and tryFunc2
to be "test"
.我得到
tryFunc1
是10
和tryFunc2
是"test"
。
Technically you only need either the OVERLAPPABLE
or OVERLAPPING
pragma, but I believe that it is good practice to have both in this case... Also, I suppose this is the behaviour that you want, but note that this just gets the first of whatever type you are looking for (so getInstance (10, (20, ())) :: Int
gives me 10
and not 20
)从技术上讲,您只需要
OVERLAPPABLE
或OVERLAPPING
编译指示,但我相信在这种情况下同时拥有两者是一种很好的做法……另外,我想这是您想要的行为,但请注意,这只是获得第一个您正在寻找的类型(所以getInstance (10, (20, ())) :: Int
给我10
而不是20
)
Good source of info is the ticket tracking the feature's creation.信息的良好来源是门票跟踪功能的创作。
I know some people don't like UndecidableInstances
but that's what I like to do in this type of situation: use a closed type family
to make it explicit that the choice is fully deterministic.我知道有些人不喜欢
UndecidableInstances
但这就是我在这种情况下喜欢做的:使用封闭的type family
来明确选择是完全确定的。
The idea is to have the type family compute a boolean flag making it clear which branch should be taken by the typeclass resolution mechanism.这个想法是让类型族计算一个布尔标志,明确类型类解析机制应该采用哪个分支。 The
UndecidableInstance
extension is needed because of the Provider ab (AtHead ab) =>
constraint even though it's harmless.由于
Provider ab (AtHead ab) =>
约束,即使它是无害的,也需要UndecidableInstance
扩展。
{-# 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.