制作类型类,不能从上下文中推断
Making a typeclass, cannot deduce from context
我正在使用 Servant 库,我想自动将结果映射到错误代码中。仆人期望类型:Either (Int, String) a
。
例如,如果我有一个模型函数类型:IO (Maybe User)
。我想把它变成 (404, "Not Found")
on Nothing,如果有的话 User
。
为此,我正在编写一个类型类!
class Servile a where
toStatus :: ToJSON val => a -> Either (Int, String) val
instance ToJSON a => Servile (Maybe a) where
toStatus Nothing = Left (404, "Not Found")
toStatus (Just a) = Right a
我还有其他实例想写,但是这个给了我错误:
Could not deduce (a ~ val)
from the context (ToJSON a)
bound by the instance declaration at Serials/Api.hs:90:10-38
or from (ToJSON val)
bound by the type signature for
toStatus :: ToJSON val => Maybe a -> Either (Int, String) val
at Serials/Api.hs:91:5-12
‘a’ is a rigid type variable bound by
the instance declaration at Serials/Api.hs:90:10
‘val’ is a rigid type variable bound by
the type signature for
toStatus :: ToJSON val => Maybe a -> Either (Int, String) val
at Serials/Api.hs:91:5
Relevant bindings include
a :: a (bound at Serials/Api.hs:92:20)
toStatus :: Maybe a -> Either (Int, String) val
(bound at Serials/Api.hs:91:5)
In the first argument of ‘Right’, namely ‘a’
In the expression: Right a
正确的做法是什么?
我跳上了#haskell,因为我没有很好地表达问题。问题是它不能改变任何 a
来产生这样的 val
。事实证明 ToJSON
与此问题无关。
这有效:请注意,我将 toStatus
更改为 a val
而不是 a
,并删除了类型变量的实例。
class ToStatus a where
toStatus :: a val -> Either (Int, String) val
instance ToStatus Maybe where
toStatus Nothing = Left (404, "Not Found")
toStatus (Just v) = Right v
instance Show a => ToStatus (Either a) where
toStatus (Left a) = Left (500, "Server Error: " <> show a)
toStatus (Right v) = Right v
另一个解决方案,如果你想使用一些非参数化类型,是使用 TypeFamilies
:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
module Temp where
import Data.Monoid
class ToStatus a where
type Val a
toStatus :: a -> Either (Int, String) (Val a)
instance ToStatus (Maybe a) where
type Val (Maybe a) = a
toStatus Nothing = Left (404, "Not Found")
toStatus (Just v) = Right v
instance Show a => ToStatus (Either a b) where
type Val (Either a b) = b
toStatus (Left e) = Left (500, "Server Error: " <> show e)
toStatus (Right v) = Right v
instance ToStatus String where
type Val String = ()
toStatus "200 Success" = Right ()
toStatus err = Left (500, err)
我正在使用 Servant 库,我想自动将结果映射到错误代码中。仆人期望类型:Either (Int, String) a
。
例如,如果我有一个模型函数类型:IO (Maybe User)
。我想把它变成 (404, "Not Found")
on Nothing,如果有的话 User
。
为此,我正在编写一个类型类!
class Servile a where
toStatus :: ToJSON val => a -> Either (Int, String) val
instance ToJSON a => Servile (Maybe a) where
toStatus Nothing = Left (404, "Not Found")
toStatus (Just a) = Right a
我还有其他实例想写,但是这个给了我错误:
Could not deduce (a ~ val)
from the context (ToJSON a)
bound by the instance declaration at Serials/Api.hs:90:10-38
or from (ToJSON val)
bound by the type signature for
toStatus :: ToJSON val => Maybe a -> Either (Int, String) val
at Serials/Api.hs:91:5-12
‘a’ is a rigid type variable bound by
the instance declaration at Serials/Api.hs:90:10
‘val’ is a rigid type variable bound by
the type signature for
toStatus :: ToJSON val => Maybe a -> Either (Int, String) val
at Serials/Api.hs:91:5
Relevant bindings include
a :: a (bound at Serials/Api.hs:92:20)
toStatus :: Maybe a -> Either (Int, String) val
(bound at Serials/Api.hs:91:5)
In the first argument of ‘Right’, namely ‘a’
In the expression: Right a
正确的做法是什么?
我跳上了#haskell,因为我没有很好地表达问题。问题是它不能改变任何 a
来产生这样的 val
。事实证明 ToJSON
与此问题无关。
这有效:请注意,我将 toStatus
更改为 a val
而不是 a
,并删除了类型变量的实例。
class ToStatus a where
toStatus :: a val -> Either (Int, String) val
instance ToStatus Maybe where
toStatus Nothing = Left (404, "Not Found")
toStatus (Just v) = Right v
instance Show a => ToStatus (Either a) where
toStatus (Left a) = Left (500, "Server Error: " <> show a)
toStatus (Right v) = Right v
另一个解决方案,如果你想使用一些非参数化类型,是使用 TypeFamilies
:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
module Temp where
import Data.Monoid
class ToStatus a where
type Val a
toStatus :: a -> Either (Int, String) (Val a)
instance ToStatus (Maybe a) where
type Val (Maybe a) = a
toStatus Nothing = Left (404, "Not Found")
toStatus (Just v) = Right v
instance Show a => ToStatus (Either a b) where
type Val (Either a b) = b
toStatus (Left e) = Left (500, "Server Error: " <> show e)
toStatus (Right v) = Right v
instance ToStatus String where
type Val String = ()
toStatus "200 Success" = Right ()
toStatus err = Left (500, err)