2017-06-04 88 views
0

跟着我的previous question我發現了generic-deriving包,它似乎有很多我需要的積木。實現gEnumToString函數被簡化爲一行。但是,我正在與gEnumFromString功能問題:如何獲得一個字符串的枚舉構造函數?

{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE TypeOperators #-} 
{-# LANGUAGE DataKinds #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE PolyKinds #-} 
{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE StandaloneDeriving #-} 
{-# LANGUAGE TemplateHaskell #-} 
{-# LANGUAGE TypeApplications #-} 
{-# LANGUAGE TypeFamilies #-} 

import Text.Read 
import GHC.Generics 
import Generics.Deriving 
import Control.Lens 
import Data.Default as DD 
import   Data.Aeson.Casing 
import Data.Aeson.Types (camelTo2) 

data Options = Options 
    { 
    optConstructorTagModifier :: String -> String 
    } deriving (Generic) 

instance DD.Default Options where 
    def = Options 
    { 
     optConstructorTagModifier = (camelTo2 '_') 
    } 

makeLensesWith abbreviatedFields ''Options 

gEnumToString :: (ConNames (Rep a), Generic a) => Options -> a -> String 
gEnumToString opt x = (opt ^. constructorTagModifier) $ conNameOf x 

gEnumFromString :: forall a . (Generic a, Enum' (Rep a), ConNames (Rep a)) 
       => Options -> String -> Maybe a 
gEnumFromString opt s = lookup s lookupTable 
    where 
    lookupTable :: [(String, a)] 
    lookupTable = (zipWith (,) (conNames undefined) genumDefault) 

此代碼不能與下面的錯誤編譯。儘管我試圖通過使用ScopedTypeVariables並明確提供forall a(如答案中給出的建議之一所述)來限制conNames undefined的類型。我究竟做錯了什麼?

168 33 error   error: 
    • Could not deduce (Generic a0) arising from a use of ‘conNames’ 
     from the context: (Generic a, Enum' (Rep a), ConNames (Rep a)) 
     bound by the type signature for: 
        gEnumFromString :: (Generic a, Enum' (Rep a), ConNames (Rep a)) => 
             Options -> String -> Maybe a 
     at /Users/saurabhnanda/projects/vl-haskell/.stack-work/intero/intero784UVH.hs:(163,1)-(164,47) 
     The type variable ‘a0’ is ambiguous 
     These potential instances exist: 
     instance Generic (Either a b) -- Defined in ‘GHC.Generics’ 
     instance forall a k (b :: k). Generic (Const a b) 
      -- Defined in ‘Data.Functor.Const’ 
     instance Generic (Identity a) -- Defined in ‘Data.Functor.Identity’ 
     ...plus 31 others 
     ...plus 201 instances involving out-of-scope types 
     (use -fprint-potential-instances to see them all) 
    • In the second argument of ‘zipWith’, namely 
     ‘(conNames undefined)’ 
     In the expression: (zipWith (,) (conNames undefined) genumDefault) 
     In an equation for ‘lookupTable’: 
      lookupTable = (zipWith (,) (conNames undefined) genumDefault) (intero) 
+0

可能的重複[爲什麼這個函數在where子句中使用範圍類型變量不是typecheck?](https://stackoverflow.com/questions/36989329/why-does-this-function-that-uses- a-scoped-type-variable-in-a-where-clause-not-ty) –

回答

2

要使類型變量的範圍,利用擴展ScopedTypeVariables,你需要一個明確的forall a.

E.g.

gEnumFromString :: forall a. (Generic a, Enum' (Rep a), ConNames (Rep a)) 
       => Options -> String -> Maybe a 
gEnumFromString opt s = lookup s lookupTable 
    where 
    lookupTable :: [(String, a)] 
    lookupTable = zipWith (,) (conNames (undefined :: a)) genumDefault 

沒有必要重複類型的限制,因爲這些都是由a「攜帶」反正。

+0

我也試過這個。仍然不起作用。我已經編輯了上面的問題以包含顯式的'forall a'和新的錯誤消息。 –

+0

奇怪的是,下面編譯。我不知道爲什麼! 'lookupTable =(zipWith(,)(conNames(undefined :: a))genumDefault)' –

+0

@SaurabhNanda啊,你必須以某種方式將'a'傳遞給'conNames'。你想像的是'conNames(undefined :: Bool)',這將會輸入check,併產生列表'[「False」,「True」]',我想。所以,當你調用'conNames'時,你必須指定想要的類型 - 它不能從上下文中推導出來。 – chi