2016-01-10 5 views
0

Итак, моя цель - вызвать немонодическую функцию и вернуть ее случайное значение.Получение случайных чисел из немонодических функций

getNums :: [Int] -- this only works when the type signature is "IO [Int]" 
getNums = getListFromIO 10 

getListFromIO :: Int -> IO [Int] 
getListFromIO n = do 
    gen <- newStdGen 
    return $ generateList n gen 

generateList :: Int -> StdGen -> [Int] 
generateList n gen = take n $ randomRs (1,9) $ gen 

Если я позвоню getListFromIO, все будет хорошо; Я получаю свой драгоценный случайный список целых чисел, и каждый раз он отличается. Но каждая функция, которая его вызывает, должна использовать IO [Int] это подпись типа. Я не хочу этого.

Как я могу структурировать это, чтобы получить случайный список номеров типа [Int]?

+5

Вы не можете. Вся цель типа «IO» заключается в том, чтобы вы не могли этого сделать. Если вы * можете *, это нарушит ваши программы. Вместо этого вы должны спросить, как использовать 'getNums :: IO [Int]' таким образом, чтобы вы могли работать с значением '[Int]', которое оно вычисляет в ваших других функциях. Это то, для чего нужны методы типа Monad и 'Functor'. –

+0

Если вы чувствуете себя авантюрно, вы можете использовать ['unsafePerformIO'] (https://hackage.haskell.org/package/base-4.0.0.0/docs/System-IO-Unsafe.html). Обратите внимание на ** небезопасный бит **. – fjarri

+5

Пожалуйста, не рекомендуем 'unsafePerformIO' тем, кто не понимает опасных последствий его использования или идиоматического способа написания Haskell без него. Это вредно. –

ответ

4

Вы не можете, не должны и не должны этого делать.

Вы утверждаете, что все функции, которые используют getNums :: IO [Int], должны иметь IO в их типе. Это просто неправда. Вы можете работать со значением [Int] что IO [Int] действие вычисляет в ряде способов, например:

main = do 
    ints <- getNums -- ints is now of type [Int] 
    return (map (+1) ints) -- map (+1) does not have IO in its type. 

Это одна из причин, по которым были введены Functor, Applicative и Monad классов типов: обеспечить последовательный способ работать с такими вещами, как IO [Int], если вы хотите избежать использования IO везде.

+0

Это помогает. Мне нужно подумать о том, как это сделать в моем коде, но я вижу, где это происходит сейчас. –

+1

@MarkKaravan Простой способ взглянуть на это: вы не получаете данные из IO и отправляете их на свои функции. В вашем коде верхнего уровня используются комбинаторы монады, чтобы вставлять чистые функции * в * монаду IO для работы с данными, в которых она живет. (Или напишите do block, для чего это синтаксический сахар). – Ben

2

Цель IO - не допускать побочных эффектов в чистом коде. Весь непустой код должен выполняться в монаде IO. Случайность также основана на состоянии или чтении из внешнего ресурса (глобального состояния). Так что вы пытаетесь без хаков. И я настоятельно призываю вас не использовать хаки. Такие хаки полностью обходят безопасность, предлагаемую системой типов; они должны ограничиваться только локализованными безопасными хаками, в первую очередь, когда система типов не является умной и не нуждается в некоторых убеждениях. Но это не так, потому что чистая функция преднамеренно ведет себя как нечистая, но не является локализованным взломом, поскольку любой код, который ее вызывает, наследует сломанную семантику, не имея возможности отслеживать, где заканчивается чистота, начинается чистота.

Лучше всего, если вы переработаете свой код, чтобы не генерировать случайные числа в чистом коде. Просто передайте случайность с точки высокого уровня в вашей иерархии кода, которая живет в IO.

+1

Вы могли бы предложить альтернативу ... – luqui

+0

Возможно, я должен добавить контекст. Я создаю генератор судоку CLI, который выдает произвольную доску. Является ли это более Haskellian для 1) запрашивать пользователя для явного случайного ввода или 2) проходить вокруг 'IO Type' во всем коде? –

+0

@MarkKaravan Ложная дихотомия. Это больше Хаскеллиан не делает ни того, ни другого. См. Мой ответ для альтернативы. –

 Смежные вопросы

  • Нет связанных вопросов^_^