2015-02-20 13 views
28

kod robocza:równe (=) Vs strzałka w lewo (<-) symbole w Haskell

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

kod Upaść:

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

Kiedy próbowałem, to wyrzucił błąd jako:

a.hs: 6: 18: błąd analizy na wejściu '='

Czym różni się <- od =?

+9

Musisz zrozumieć, jak Monad de-sugaring ma pojęcie o tym, jak to pasuje. – Sibi

+2

@Sibi jest technicznie prawdą, ale uważam, że powinno być możliwe udzielenie odpowiedzi na to pytanie bez wspominania o M-word. W końcu m *** ds jest tylko jednym ze sposobów radzenia sobie z IO w czystym języku. Haskell nadal miałby separację między czystym a nieczytelnym kodem, nawet gdyby nie miał wbudowanego m **** ds, a ten problem (lub taki jak on) nadal istniałby. –

+0

@ChrisTaylor Chociaż można odpowiedzieć na to pytanie bez wyjaśnienia Monad, naprawdę wątpię, czy pomoże to OP w uchwyceniu tej koncepcji. Biorąc to pod uwagę, tutaj jest wiele dobrych odpowiedzi. – Sibi

Odpowiedz

52

Aby zrozumieć prawdziwą różnicę, musisz zrozumieć monady i usuwanie opisywane przez @rightfold w ich odpowiedzi.

Dla konkretnego przypadku monady IO, jak w przykładzie getArgs, szorstka, ale przydatne intuicja może być wykonane w następujący sposób:

  • x <- actionbiegnie IO action, dostaje swój wynik i wiąże to do x
  • let x = action definiuje x jako odpowiednik action, ale niczego nie uruchamia. Później możesz użyć y <- x, co oznacza y <- action.

Programatory pochodzące z języków nadrzędnych, które pozwalają zamknięcia, może zwrócić tę szorstką porównania równolegle z 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 

Znowu: porównanie to skupia się na IO, tylko. W przypadku innych monad musisz sprawdzić, czym naprawdę jest >>= i pomyśleć o usunięciu do.

+5

I tak po prostu, Monady w końcu klikają. W tym poście jest Tyle zwycięstw. –

38
do 
    x <- y 
    f x 

odpowiada:

y >>= \x -> f x 

do 
    let x = y 
    f x 

odpowiada

f y 

tj let/= ogranicza monadycznego nie wiążą natomiast <- .

+0

Powiedzenie 'let x = y in f x' (które tak naprawdę * jest * równoważne z 2. wyrażeniem do do) jest" równoznaczne "z" f y "jest niebezpieczne, głównie dlatego, że dla czytelnika może nie być oczywiste, co oznacza" równoważny "; wartości tych dwóch wyrażeń są takie same, ale ich semantyka może być inna. – user2407038

+8

@ user2407038 nie, to dokładnie to samo. Nie ma jednej różnicy. – rightfold

10

Kod nie jest kompilowany, ponieważ typy nie są zgodne. Chodźmy załadować sesję GHCI i spojrzeć na rodzaje funkcji jesteś korzystających -

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

Więc writeFile chce FilePath i String. Chcesz uzyskać String od readFile - ale readFile zwraca IO String zamiast String.

Haskell jest bardzo prymitywnym językiem.Ma rozróżnienie między czystymi funkcjami (które dają te same wyjścia za każdym razem, gdy są wywoływane z tymi samymi argumentami) i nieczytelnym kodem (co może dawać różne wyniki, np. Jeśli funkcja zależy od niektórych danych wprowadzonych przez użytkownika). Funkcje dotyczące wejścia/wyjścia (IO) zawsze mają typ zwrotu oznaczony jako IO. System typów zapewnia, że ​​nie można użyć nieczytelnego kodu IO w czystych funkcjach - na przykład zamiast zwracać String funkcja readFile zwraca IO String.

W tym miejscu ważna jest notacja <-. Dzięki niemu możesz uzyskać numer String wewnątrz IO i zapewnia to, że cokolwiek robisz z tym ciągiem, definiowana funkcja będzie zawsze oznaczona jako IO. Porównaj następujące -

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

który nie jest to, co chcemy, aby ten

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

co jest, co chcemy. Jeśli kiedykolwiek masz funkcję, która zwraca IO a i chcesz uzyskać dostęp do a, musisz użyć <-, aby przypisać wynik do nazwy. Jeśli twoja funkcja nie zwraca IO a, lub jeśli nie chcesz dostać się do a wewnątrz IO, możesz po prostu użyć =.

+1

Kod OP zawiera * błąd składni *. Wygląda na to, że prawdopodobnie należy zająć się nim przed błędem typu, który się za nim kryje. – dfeuer

+0

Noted, thanks :) –

7
let x = readFile file1 

Dzieje akcję "readFile file1" i przechowuje działania w x.

x <- readFile file1 

Ten wykonuje akcja "readFile file1" i przechowuje Rezultat działania w x.

W pierwszym przykładzie, x jest niezrealizowanym obiektem akcji We/Wy. W drugim przykładzie, x jest zawartością pliku na dysku.