簡體   English   中英

使用帶有約束字段的數據類型代替約束

[英]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實例存在某種效率。 我們可以為Barbar更多有啟發性的名字。

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"

隱藏IsString

如果你想轉動類型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 => afromString 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的東西,它還允許mappendmconcatmemptyMonoid操作。 這意味着類型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級類型, mconcatmappend無法以無點樣式編寫。

instance Monoid MonoidalString where
    mempty = MonoidalString mempty
    (MonoidalString x) `mappend` (MonoidalString y) = MonoidalString (x `mappend` y)
    mconcat ms = MonoidalString (mconcat (map toStringPlus ms))

MonoidalString也可以是IsStringToStringPlus實例,其方式與上一節中的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來獲取在IsStringMonoid所有實例上具有多態性的東西。

鑒於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 => aD只要有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

我們也可以傳遞給單形MonoidalStringexisting :: MonoidalString

    existing = ("MS" :: MonoidalString)
bar existing                            :: (Monoid a, IsString a) => a

暫無
暫無

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

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