Применение экзистенциально типизированной функции к достаточно полиморфному аргументу c - PullRequest
1 голос
/ 26 января 2020

Предположим, у меня есть очень простой способ генерации потока из функции перехода состояния:

machine :: (s -> s) -> (s -> a) -> s -> [a]
machine next proj s0 = proj <$> iterate next s0

В какой-то момент я хотел бы использовать эту функцию, не заботясь о точном выборе s. Допустим, у меня есть тип для моего состояния, который имеет параметр типа:

data Foo a = Foo1 Int | Foo2 a

, и у меня есть функция перехода состояния, которая имеет в виду a:

{-# LANGUAGE ExistentialQuantification #-}

data Step = forall a. MkStep (Foo a -> Foo a)

step :: Step
step = _

I осторожно наблюдаю мое состояние в параметре типа-агности c way:

observe :: forall a. Foo a -> Int
observe (Foo1 x) = x
observe _ = 0

Мое начальное состояние также является параметром-агности типа c:

initial :: forall a. Foo a
initial = Foo1 0

Учитывая все Я надеялся, что в этих благоприятных условиях я смогу упаковать состояние initial, переход step и observe, так как оба экземпляра initial и observe могут быть созданы в соответствии с выбором функции step a, но без игры в кости:

test :: [Int]
test = let MkStep next = step in machine next observe initial
    • Couldn't match expected type ‘p’
                  with actual type ‘Foo a -> Foo a’
        because type variable ‘a’ would escape its scope
      This (rigid, skolem) type variable is bound by
        a pattern with constructor:
          MkStep :: forall a. (Foo a -> Foo a) -> Step,
        in a pattern binding
        at StateMachine.hs:21:12-22
    • In the pattern: MkStep next
      In a pattern binding: MkStep next = step
      In the expression:
        let MkStep next = step in machine next observe initial ```

Есть ли способ достичь этого без изменения типа machine? [1]

Интересно, что использование Step в качестве параметра работает:

test2 :: Step -> [Int]
test2 (MkStep next) = machine next observe initial

Но я бы не стал переписывать свой код в этом направлении.

[1] Здесь я использую списки , но для моего реального случая использования machine - это комбинатор сигналов Cla sh из стандартной библиотеки.

1 Ответ

5 голосов
/ 26 января 2020
test :: [Int]
test = case step of
   MkStep next -> machine next observe initial
   -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ case instead of let

По сути, вы должны использовать case или другую форму "строгого" сопоставления с образцом, чтобы привести экзистенциальный тип в область видимости. Ставка, введенная let, ленива и не сделает этого.

...