Есть ли способ высушить применение полиморфной функции к числу объектов с различными типами параметров в Haskell? - PullRequest
0 голосов
/ 09 октября 2018

Это упрощенная версия проблемы, с которой я сталкиваюсь (так что функционально это не имеет смысла, но это проблема типа).

module EGBase where
import           Prelude

newtype SomeData a b = SomeData String

module EGChild where
import           EGBase
import           Prelude

myData :: SomeData Int Int
myData = SomeData "Child"

module EGChild1 where
import           EGBase
import           Prelude

myData :: SomeData Int String
myData = SomeData "Child 1"

module EGMain where
import           EGBase
import           EGChild
import           EGChild1
import           Prelude

worker1 :: SomeData a b -> IO ()
worker1 _ = putStrLn "Hello from Worker 1"

worker2 :: SomeData a b -> IO ()
worker2 _ = putStrLn "Hello from Worker 2"

mergeThem :: [IO ()] -> IO ()
mergeThem = foldl (>>) (pure ())

main1 :: IO ()
main1 = mergeThem [
                worker1 EGChild.myData,
                worker1 EGChild1.myData
              ]

main2 :: IO ()
main2 = mergeThem [
                worker2 EGChild.myData,
                worker2 EGChild1.myData
              ]

Приведенные выше компиляции, но в реальном приложении у меня потенциально будет много разных работников, которых я хочу подать насписок множества модулей (создание множества версий приложения).Я хотел бы снять, а не жестко закодировать рабочую функцию.

В качестве первого шага я попытался просто вытащить жестко закодированного работника из списка, но это не сработало, потому что элементы списка не были одинаковыми.введите:

-- different types in list [SomeData Int Int, SomeData Int String]
main2Dry :: IO ()
main2Dry = mergeThem $ worker2 <$> [
                         EGChild.myData,
                         EGChild1.myData
                        ]

Что мне действительно нужно, так это что-то вроде этого, но это не будет работать по той же причине, что и выше:

mainShared :: (SomeData a b -> IO ()) -> IO ()
mainShared worker = mergeThem $ worker <$> [
                     EGChild.myData,
                     EGChild1.myData
                   ]

Следующее тоже будет хорошо, но я думаю,проблема здесь в том, что конкретный тип рабочей функции выводится из первого элемента в списке, что означает, что существуют проблемы, когда рабочий применяется ко второму элементу:

mainShared :: (SomeData a b -> IO ()) -> IO ()
mainShared worker = mergeThem [
                       worker EGChild.myData,
                       worker EGChild1.myData
                     ]
--
-- does not compile
--
-- src\EGMain.hs:50:28-42: error:
--     * Couldn't match type `b' with `String'
--       `b' is a rigid type variable bound by
--         the type signature for:
--           mainShared :: forall a b. (SomeData a b -> IO ()) -> IO ()
--         at src\EGMain.hs:47:1-46
--       Expected type: SomeData a b
--         Actual type: SomeData Int String
--     * In the first argument of `worker', namely `EGChild1.myData'
--       In the expression: worker EGChild1.myData
--       In the first argument of `mergeThem', namely
--         `[worker EGChild.myData, worker EGChild1.myData]'

Так вот, что я пытаюсьдобиться возможности в Haskell?

Ответы [ 2 ]

0 голосов
/ 11 октября 2018

Использование RankNTypes в соответствии с предложением Джонаса Дюрегора работало:

{-# LANGUAGE RankNTypes #-}

worker1 :: forall a b. SomeData a b -> IO ()
worker1 _ = putStrLn "Hello from Worker 1"

worker2 :: forall a b. SomeData a b -> IO ()
worker2 _ = putStrLn "Hello from Worker 2"

mergeThem :: [IO ()] -> IO ()
mergeThem = foldl (>>) (pure ())

mainShared :: (forall a b. SomeData a b -> IO ()) -> IO ()
mainShared worker = mergeThem [
                    worker EGChild.myData,
                    worker EGChild1.myData
                    ]

demo1 = mainShared worker1
-- > Hello from Worker 1
-- > Hello from Worker 1

demo2 = mainShared worker2
-- > Hello from Worker 2
-- > Hello from Worker 2

Я нашел эту ссылку на RankNTypes полезной https://ocharles.org.uk/blog/guest-posts/2014-12-18-rank-n-types.html.

0 голосов
/ 09 октября 2018

Невозможно в стандартном Haskell, но есть несколько расширений, которые позволят вам сделать это, включая GADT и RankNTypes.

Обходной путь, который может работать в вашем упрощенном примере, но, возможно, не в вашем реальном коде, заключается впросто удалите фиктивные параметры типа:

erase :: SomeData a b -> SomeData () ()
erase (SomeData x) = SomeData x

mainShared :: (SomeData () () -> IO ()) -> IO ()
mainShared worker = mergeThem $ map worker [
                       erase EGChild.myData,
                       erase EGChild1.myData
                     ]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...