Подтвердить что ты не робот

Равный (=) символ левой стрелки (<-) в символе haskell

Рабочий код:

import System
main = do
     [file1, file2] <- getArgs
     --copy file contents
     str <- readFile file1
     writeFile file2 str

Код сбоя:

import System
main = do
       [file1, file2] = getArgs
       str = readFile file1
       writeFile file2 str

Когда я попытался, он сделал ошибку как:

a.hs: 6: 18: ошибка синтаксического анализа на входе '='

Итак, насколько отличается <- от =?

4b9b3361

Ответ 1

Чтобы понять реальную разницу, вам нужно понять монады и десураринг, описанный @rightfold в их ответе.

Для конкретного случая монады IO, как и в вашем примере getArgs, грубую, но полезную интуицию можно сделать следующим образом:

  • x <- action запускает IO action, получает его результат и привязывает его к x
  • let x = action определяет x как эквивалент action, но ничего не запускает. Позже вы можете использовать y <- x значение y <- action.

Программисты, исходящие из императивных языков, которые допускают замыкания, могут провести это грубое параллельное сравнение с Javascript:

var action = function() { print(3); return 5; }

// roughly equivalent to x <- action
print('test 1')
var x = action()  // output:3
// x is 5

// roughly equivalent to let y = action
print('test 2')
var y = action    // output: nothing
// y is a function

// roughly equivalent to z <- y
print('test 3')
var z = y()       // output:3
// z is 5

Опять же: это сравнение фокусируется только на IO. Для других монадов вам нужно проверить, что на самом деле есть >>=, и подумать о desugaring do.

Ответ 2

do
    x <- y
    f x

эквивалентно:

y >>= \x -> f x

do
    let x = y
    f x

эквивалентно

f y

то есть. let/= не выполняет монадическое связывание, тогда как <- делает.

Ответ 3

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

> :t writeFile
writeFile :: FilePath -> String -> IO ()
>
> :t readFile
readFile :: FilePath -> IO String

Итак, writeFile хочет a FilePath и a String. Вы хотите получить String от readFile, но readFile возвращает IO String вместо String.

Haskell - очень принципиальный язык. Он имеет различие между чистыми функциями (которые дают одинаковые выходы каждый раз, когда они вызываются с теми же аргументами) и нечистым кодом (который может давать разные результаты, например, если функция зависит от некоторого ввода пользователя). Функции, имеющие отношение к входному/выходному (IO), всегда имеют тип возврата, который помечен IO. Система типов гарантирует, что вы не можете использовать нечистый код IO внутри чистых функций - например, вместо возврата String функция readFile возвращает IO String.

Здесь важна нотация <-. Он позволяет вам получить String внутри IO и гарантирует, что независимо от того, что вы делаете с этой строкой, функция, которую вы определяете, всегда будет отмечена IO. Сравните следующее -

> let x = readFile "tmp.txt"
> :t x
x :: IO String

который не является тем, что мы хотим, к этому

> y <- readFile "tmp.txt"
> :t y
y :: String

что мы и хотим. Если у вас когда-либо есть функция, которая возвращает IO a, и вы хотите получить доступ к a, вам нужно использовать <-, чтобы присвоить результат имени. Если ваша функция не возвращает IO a, или если вы не хотите попасть в a внутри IO, вы можете просто использовать =.

Ответ 4

let x = readFile file1

Это принимает действие "readFile file1" и сохраняет действие в x.

x <- readFile file1

Выполняет действие "readFile file1" и сохраняет результат действия в x.

В первом примере x - это объект недействительного объекта ввода-вывода. Во втором примере x - это содержимое файла на диске.