简体   繁体   English

Haskell 递归类型类

[英]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编译指示,并开始使用每个实例的OVERLAPPINGOVERLAPPABLE编译指示。 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" .我得到tryFunc110tryFunc2"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 )从技术上讲,您只需要OVERLAPPABLEOVERLAPPING编译指示,但我相信在这种情况下同时拥有两者是一种很好的做法……另外,我想这是您想要的行为,但请注意,这只是获得第一个您正在寻找的类型(所以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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM