簡體   English   中英

Haskell 遞歸類型類

[英]Haskell Recursive Type Classes

我想創建一個基於元組的遞歸實例類型。 我正在尋找的是與此類似的東西:

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 無法解決該實例。 任何原因?

解決方案是停止使用已棄用的OverlappingInstances編譯指示,並開始使用每個實例的OVERLAPPINGOVERLAPPABLE編譯指示。 僅此更改:

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

我得到tryFunc110tryFunc2"test"


從技術上講,您只需要OVERLAPPABLEOVERLAPPING編譯指示,但我相信在這種情況下同時擁有兩者是一種很好的做法……另外,我想這是您想要的行為,但請注意,這只是獲得第一個您正在尋找的類型(所以getInstance (10, (20, ())) :: Int給我10不是20

信息的良好來源是門票跟蹤功能的創作。

我知道有些人不喜歡UndecidableInstances但這就是我在這種情況下喜歡做的:使用封閉的type family來明確選擇是完全確定的。

這個想法是讓類型族計算一個布爾標志,明確類型類解析機制應該采用哪個分支。 由於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