簡體   English   中英

存儲和檢索某個類型類的對象 - 這可能嗎? 如果有,怎么樣?

[英]Store and retrieve an object of some typeclass - is it possible? If yes, how?

假設我有一些類型類Foo和一些數據類型FooInst ,它是Foo實例:

class Foo a where
  foo :: a -> String

data FooInst = FooInst String

instance Foo FooInst where
    foo (FooInst s) = s

現在,我想定義一種數據類型,它存儲一個類型在Foo類型類中的對象,並且能夠從該數據類型中提取該對象並使用它。

我找到的唯一方法是使用GADT和Rank2Types語言擴展並定義數據類型,如下所示:

data Container where
  Container :: { content :: Foo a => a } -> Container

但問題是,我無法使用content選擇器從Container中獲取內容:

cont :: Container
cont = Container{content = FooInst "foo"}

main :: IO ()
main = do
  let fi = content cont
  putStrLn $ foo fi

導致編譯錯誤

Cannot use record selector ‘content’ as a function due to escaped type variables
Probable fix: use pattern-matching syntax instead

但是當我修改let ...行時

let Conainer fi = cont

我得到一個相當有趣的錯誤

My brain just exploded
I can't handle pattern bindings for existential or GADT data constructors.
Instead, use a case-expression, or do-notation, to unpack the constructor.

如果我再次嘗試修改let ...行以使用case-expression

let fi = case cont of
           Container x -> x

我得到了一個不同的錯誤

Couldn't match expected type ‘t’ with actual type ‘a’
  because type variable ‘a’ would escape its scope
This (rigid, skolem) type variable is bound by
  a pattern with constructor
    Container :: forall a. (Foo a => a) -> Container,
  in a case alternative
  at test.hs:23:14-24

那么, 我如何存儲一個typeclassed的東西並將其取回?

附:

data Container where
    Container :: {content :: Foo a => a} -> Container

甚至沒有強制執行類型類約束。 那是

void :: Container
void = Container {content = 42 :: Int}

類型檢查,即使42 :: Int 不是 Foo的實例。

但如果你改為:

data Container where
    Container :: Foo a => {content :: a} -> Container
  1. 你不再需要Rank2Types語言擴展。
  2. 強制執行類型類約束; 因此,上面的void示例將不再進行類型檢查。
  3. 此外,您可以在具有模式匹配的內容上調用foo (或具有簽名Foo a => a -> ...任何其他函數):

     case cont of Container {content = a} -> foo a 

例如

main :: IO ()
main = do
  case cont of
    Container fi -> putStrLn $ foo fi

你需要在一個表達式中封裝你對存在類型字段fi ,該表達式的類型不依賴於fi的類型; 這里putStrLn $ foo fi ,其類型為IO ()

這個例子是沒用的,因為你可以對Containercontent字段做的唯一事情就是調用foo ,所以你也可以在構造容器之前調用foo並給出字段類型String 但是,如果Foo具有類似a -> a -> a類型的操作,或者Container具有涉及相同存在量化變量的類型的多個字段等,則更有趣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM