Как обойти проблему с неоднозначностью, когда мономорфное ограничение включено *?
Итак, изучая 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