Функциональная зависимость с правилом специализации

Я хочу написать правило перезаписи специализации для комбинатора Megaparsec, чтобы правило срабатывало только тогда, когда тип ввода — ByteString.

{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}

import Data.Void
import Text.Megaparsec
import qualified Data.ByteString as B

combin :: forall e s m a. (MonadParsec e s m)
    => m a
    -> m a
combin = label "String generic"

combinByteString :: forall e s m a. (MonadParsec e s m, s ~ B.ByteString)
    => m a
    -> m a
combinByteString = label "ByteString specialized"

main = do
    parseTest (combin empty :: Parsec Void String String) ""
    parseTest (combin empty :: Parsec Void B.ByteString String) ""

{-# NOINLINE combin #-}
{-# NOINLINE combinByteString #-}
{-# RULES "combin/ByteString" combin = combinByteString #-}

Когда я пытаюсь построить это, это терпит неудачу:

$ cabal v2-run
Build profile: -w ghc-8.6.5 -O1
Main.hs:25:40: error:
    • Couldn't match type ‘s’ with ‘B.ByteString’
        arising from a functional dependency between constraints:
          ‘MonadParsec e B.ByteString m’
            arising from a use of ‘combinByteString’ at Main.hs:25:40-55
          ‘MonadParsec e s m’
            arising from the RULE "combin/ByteString" at Main.hs:25:11-55
      ‘s’ is a rigid type variable bound by
        the RULE "combin/ByteString"
        at Main.hs:25:11-55
    • In the expression: combinByteString
      When checking the transformation rule "combin/ByteString"
   |
25 | {-# RULES "combin/ByteString" combin = combinByteString #-}

Параметр типа входного потока s из MonadParsec имеет функциональную зависимость в параметре Monad m.

class (Stream s, MonadPlus m) => MonadParsec e s m | m -> e s where

Вот файл specialize.cabal для пробной сборки.

cabal-version:       >=1.10
name:                specialize
version:             0.1.0.0
build-type:          Simple

executable specialize
  main-is:             Main.hs
  build-depends:       base >= 4
                      ,megaparsec
                      ,bytestring
  default-language:    Haskell2010

В случае успеха вывод должен выглядеть так:

1:1:
  |
1 | <empty line>
  | ^
expecting String generic
1:1:
  |
1 | <empty line>
  | ^
expecting ByteString specialized

Совет?


person James Brock    schedule 25.10.2019    source источник
comment
Похоже, фонд хочет, чтобы s вытекало из m, поэтому у вас не может быть разных s (String или ByteString) для одного и того же m. Пробовали ли вы вместо этого исправить m, например m ~ Parsec e ByteString a?   -  person Fyodor Soikin    schedule 25.10.2019
comment
@fyodor-soykin Я пытался исправить m способами, подобными тому, что вы предлагаете, и я все еще не могу заставить его собраться, и даже если бы он построился, это было бы не совсем правильно, потому что я хочу, чтобы это работало для всех экземпляры MonadParsec, а не только ParsecT.   -  person James Brock    schedule 25.10.2019
comment
Обязательно ли иметь RULE? Можете ли вы вместо этого сделать класс типов?   -  person Fyodor Soikin    schedule 25.10.2019
comment
@FyodorSoikin Я готов попробовать любое решение, но я не хочу каким-либо образом менять общедоступную подпись типа API combin. Я не хочу добавлять какие-либо специальные ограничения класса в общедоступный API.   -  person James Brock    schedule 25.10.2019
comment
stackoverflow.com/questions/19745038/   -  person James Brock    schedule 25.10.2019
comment
Да, вы можете полностью использовать ifcxt   -  person Fyodor Soikin    schedule 25.10.2019


Ответы (1)


Это правило работает, но только для GHC 8.8.1, а не для GHC 8.6.5.

{-# LANGUAGE TypeApplications #-}

{-# RULES "combin/ByteString" forall e. forall. 
    combin @e @B.ByteString = combinByteString @e @B.ByteString
#-}

Это правило работает с GHC 8.6.5 и 8.0.2.

{-# RULES "combin/ByteString"
 forall (pa :: ParsecT e B.ByteString m a).
 combin @e @B.ByteString @(ParsecT e B.ByteString m) @a pa = 
 combinByteString @e @B.ByteString @(ParsecT e B.ByteString m) @a pa
#-}
person James Brock    schedule 26.10.2019