TL,DR; 扩展约束,ad-hoc ......?我的路线是“健忘”,或者是不相等的
各位大家好,我正在尝试制作一个可以采用约束(在我们的例子中为IsString
)的重载函数,或者具有相同约束字段的数据类型。到目前为止,这是我的代码:
{-# LANGUAGE
OverloadedStrings
, FlexibleInstances
, UndecidableInstances
, InstanceSigs
, TypeFamilies
#-}
import Data.Monoid
class Bar a where
bar :: ( IsString b
, Monoid b ) => a -> b
-- | This instance won't work.
instance ( IsString a
, Monoid a ) => RelativeUrl a where
bar :: ( IsString b
, Monoid b
, a ~ b ) => a -> b
bar = id
-- | This is the data type "extending" @IsString@
data Foo a where
Foo :: ( IsString a, Monoid a ) =>
a -> Foo a
-- | This is where my dreams end :(
instance Bar (Foo a) where
bar :: ( IsString b
, Monoid b
, a ~ b ) => a -> b
bar (Foo a) = a
我意识到实例签名不是犹太教,这就是(技术上)这不起作用的原因,但有没有其他方法可以做到这一点?理想情况下,所有对bar
的调用都可以通过上下文推断 - 例如bar "foo" :: IsString a => a
,而不必将OverloadedString限制为实际类型。
还有另一种方法可以达到这个目的吗?我愿意接受疯狂的想法:)
答案 0 :(得分:3)
Bar
类可以转换为IsString
的任何内容。我认为Monoid
实例存在某种效率。我们可以为Bar
和bar
提供更多有启发性的名称。
class ToStringPlus a where
toStringPlus :: ( IsString b,
Monoid b ) => a -> b
您希望bar "foo" :: IsString a => a
。启用OverloadedStrings
"foo" :: IsString a -> a
。您正在询问如何将已在IsString
的所有实例上已经多态的值转换为在IsString
的所有实例上具有多态性的值。您不需要像toStringPlus "foo"
这样的内容,只需使用"foo"
。
如果您想将类型forall a. IsString a => a
转换为数据类型,则可以使用GADT。它根本没用,因为forall a. IsString a => a
类型唯一可能的值是fromString x
x :: String
。此类型可以保存String
可以容纳的完全相同的值,而实用程序String
没有提供。
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
import Data.String
data AString where
AString :: (forall a. IsString a => a) -> AString
instance IsString AString where
fromString x = AString (fromString x)
instance ToStringPlus AString where
toStringPlus (AString a) = a
AString
不是很有用,因为它只能保存与String
相同的值。 ToStringPlus
类允许转换为使用String
以上内容的内容,还允许Monoid
,mappend
和mconcat
的{{1}}操作}。这意味着类型mempty
应该能够保存不同于forall a. (IsString a, Monoid a) => a
的内容。
String
data MonoidalString where
MonoidalString :: (forall a. (IsString a, Monoid a) => a) -> MonoidalString
形成MonoidalString
。请注意,由于排名N类型,Monoid
和mconcat
无法以无点样式编写。
mappend
instance Monoid MonoidalString where
mempty = MonoidalString mempty
(MonoidalString x) `mappend` (MonoidalString y) = MonoidalString (x `mappend` y)
mconcat ms = MonoidalString (mconcat (map toStringPlus ms))
也可以是MonoidalString
和IsString
的实例,其方式与上一节中的ToStringPlus
相同。
AString
这让我们在评论中为您的请求赋予意义“我正在尝试将已经多态的内容转换为instance IsString MonoidalString where
fromString x = MonoidalString (fromString x)
instance ToStringPlus MonoidalString where
toStringPlus (MonoidalString a) = a
和任何IsString
的所有实例[以及多态的内容......] ”。我们可以使用Foo
操作将Monoid
,IsString
的所有实例已经多态的内容与"poly string"
结合起来,以获得具有多态性的内容在MonoidalString
和IsString
的所有实例中。
鉴于某些内容Monoid
和existing :: MonoidalString
,我们可以将它们与"poly string" :: IsString a => a
结合使用。
mappend
我们可以使用它来制作一个小示例程序,以展示 existing :: MonoidalString
"poly string" :: IsString a => a
"poly string" `mappend` existing :: MonoidalString
toStringPlus ("poly string" `mappend` existing) :: (Monoid b, IsString b) => b
MonoidalString
如果你想创建一个接受main = do
let existing = ("MS" :: MonoidalString)
putStr . toStringPlus $ mconcat ["poly string", mempty `mappend` " ", existing]
和bar
类型参数的函数forall a. Ctx a => a
,只要有D
,你就可以这样做。然后函数的类型为instance Ctx D
。这是有效的,因为D -> ...
可以在任何需要forall a. Ctx a => a
的地方使用。
我们可以使用它为最后一个例子写一个D
。
bar
我们可以传递给bar :: (IsString a, Monoid a) => MonoidalString -> a
bar = toStringPlus
多态字符串bar
。
"foo" :: IsString a => a
我们也可以传递到单形 "foo" :: IsString a => a
bar "foo" :: (Monoid a, IsString a) => a
,MonoidalString
existing :: MonoidalString