Как обойти проблему с неоднозначностью, когда мономорфное ограничение включено *?

Итак, изучая Haskell, я натолкнулся на страшное мономорфное ограничение, довольно скоро, со следующим (в ghci):

Prelude> let f = print.show
Prelude> f 5

<interactive>:3:3:
    No instance for (Num ()) arising from the literal `5'
    Possible fix: add an instance declaration for (Num ())
    In the first argument of `f', namely `5'
    In the expression: f 5
    In an equation for `it': it = f 5

Таким образом, есть множество материалов по этому поводу, например, здесь, и это не так сложно обойти. Я могу добавить явную сигнатуру типа для f или отключить мономорфное ограничение (с помощью ":set -XNoMonomorphismRestriction" непосредственно в ghci или в файле.ghci).

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

Так что я выключил это.

Но потом я наткнулся на другую проблему:

Prelude> :set -XNoMonomorphismRestriction
Prelude> let (a,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int 

<interactive>:4:5:
    No instance for (System.Random.Random t0)
      arising from the ambiguity check for `g'
    The type variable `t0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance System.Random.Random Bool -- Defined in `System.Random'
      instance System.Random.Random Foreign.C.Types.CChar
        -- Defined in `System.Random'
      instance System.Random.Random Foreign.C.Types.CDouble
        -- Defined in `System.Random'
      ...plus 33 others
    When checking that `g' has the inferred type `System.Random.StdGen'
    Probable cause: the inferred type is ambiguous
    In the expression:
      let (a, g) = System.Random.random (System.Random.mkStdGen 4)
      in a :: Int
    In an equation for `it':
        it
          = let (a, g) = System.Random.random (System.Random.mkStdGen 4)
            in a :: Int

Это на самом деле упрощено из примера кода в книге "Real World Haskell", который не работал для меня, и которую вы можете найти на этой странице: http://book.realworldhaskell.org/read/monads.html (это глава Monads и пример функции getRandom выполняют поиск "getRandom" на этой странице).

Если я оставлю мономорфное ограничение на (или включу его), то код работает. Это также работает (с мономорфным ограничением на), если я изменяю его на:

Prelude> let (a,_) = System.Random.random (System.Random.mkStdGen 4) in a :: Int 
-106546976

или если я укажу тип "а" ранее:

Prelude> let (a::Int,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int
-106546976

но для этого второго обходного пути мне нужно включить расширение "переменные типа scoped" (с помощью ":set -XScopedTypeVariables").

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

Например, может быть, я хочу написать функцию, которая делает что-то подобное и работает с произвольными (или множественными) типами, и, конечно, в этом случае я, скорее всего, действительно хочу сохранить новое состояние генератора (в "g"),

Тогда возникает вопрос: как мне обойти этот тип проблемы в целом, без указания точного типа напрямую?

И было бы замечательно (как новичку на Haskell) получить больше информации о том, что именно здесь происходит и почему возникают эти проблемы.

1 ответ

Решение

Когда вы определяете

(a,g) = random (mkStdGen 4)

тогда даже если g сам по себе всегда имеет тип StdGen, стоимость g зависит от типа a, потому что разные типы могут отличаться в том, насколько они используют генератор случайных чисел.

Более того, когда вы (гипотетически) используете g позже, пока a был полиморфным, нет никакого способа решить, какой тип a вы хотите использовать для расчета g,

Таким образом, в отдельности, как полиморфное определение, вышесказанное должно быть запрещено g на самом деле крайне неоднозначно, и эта неоднозначность не может быть исправлена ​​на сайте использования.

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

Когда вы используете _ вместо этого, по-видимому, GHC не беспокоится об этой двусмысленности, если она не влияет на расчет a, Возможно, он мог обнаружить, что g не используется в предыдущей версии и обрабатывается аналогично, но, очевидно, это не так.

Что касается обходных путей без указания ненужных явных типов, вы можете вместо этого попробовать заменить let/where одним из методов связывания в Haskell, которые всегда мономорфны. Следующие все работы:

case random (mkStdGen 4) of
    (a,g) -> a :: Int

(\(a,g) -> a :: Int) (random (mkStdGen 4))

do (a,g) <- return $ random (mkStdGen 4)
   return (a :: Int)    -- The result here gets wrapped in the Monad
Другие вопросы по тегам