[英]Using a data type with Constrained fields in place of a Constraint
TL,DR; 延長約束,臨時......? 我的路線是“健忘”,或者是不相稱的
大家好,我正在嘗試制作一個可以采用約束(在我們的例子中是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 bar "foo" :: IsString a => a
實際類型。
還有另一種方法來實現這一目標嗎? 我對瘋狂的想法持開放態度:)
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
forall a. IsString a => a
到數據類型,你可以用GADT做到這一點。 它沒有任何用處,因為forall a. IsString a => a
類型的唯一可能值forall a. IsString a => a
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
的東西,它還允許mappend
, mconcat
和mempty
的Monoid
操作。 這意味着類型forall a. (IsString a, Monoid a) => a
forall a. (IsString a, Monoid a) => a
應該能夠保存不同於String
的東西。
data MonoidalString where
MonoidalString :: (forall a. (IsString a, Monoid a) => a) -> MonoidalString
MonoidalString
形成一個Monoid
。 請注意,由於N級類型, 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
東西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
forall a. Ctx a => a
和D
只要有instance Ctx D
就可以這樣做。 然后函數的類型是D -> ...
這是因為一個forall a. Ctx a => a
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
existing = ("MS" :: MonoidalString)
bar existing :: (Monoid a, IsString a) => a
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.