Равный (=) символ левой стрелки (<-) в символе 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: ошибка синтаксического анализа на входе '='
Итак, насколько отличается <-
от =
?
Ответы
Ответ 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
- это содержимое файла на диске.