String naar Int in Haskell

Pagina: 1
Acties:

Onderwerpen

Vraag


Acties:
  • 0 Henk 'm!

  • ONiel
  • Registratie: September 2015
  • Laatst online: 15-06 21:16
Hey

Ik probeer Haskell te leren, maar ik vindt het moeilijk omdat het mijn eerste functionele programmeertaal is.
Bij andere talen kan ik dan ook (bijna) al mijn vragen vinden op zoekmachines, maar de antwoorden omtrent Haskell vindt ik om de een of andere reden veel te ingewikkeld.

Dit is mijn code:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import System.IO

prompt txt = do
    putStr txt
    hFlush stdout
    getLine

doDouble x = x * 2

main = do
    name <- prompt "Hi, what's ya name? "
    putStrLn ("Hello " ++ name)

    nmrToDouble <- prompt "Choose a number to double: "
    nmrToDouble :: Int
    doDouble nmrToDouble


Output van ghc:
* Couldn't match expected type `IO a0' with actual type `Int'
* In a stmt of a 'do' block: nmrToDouble :: Int
In the expression:
do { name <- prompt "Hi, what's ya name? ";
putStrLn ("Hello " ++ name);
nmrToDouble <- prompt "Choose a number to double: ";
nmrToDouble :: Int;
.... }
In an equation for `main':
main
= do { name <- prompt "Hi, what's ya name? ";
putStrLn ("Hello " ++ name);
nmrToDouble <- prompt "Choose a number to double: ";
.... }

C:\users\niel\desktop\test.hs:16:5: error:
* Couldn't match type `[]' with `IO'
Expected type: IO Char
Actual type: String
* In a stmt of a 'do' block: doDouble nmrToDouble
In the expression:
do { name <- prompt "Hi, what's ya name? ";
putStrLn ("Hello " ++ name);
nmrToDouble <- prompt "Choose a number to double: ";
nmrToDouble :: Int;
.... }
In an equation for `main':
main
= do { name <- prompt "Hi, what's ya name? ";
putStrLn ("Hello " ++ name);
nmrToDouble <- prompt "Choose a number to double: ";
.... }
Ik wil de gebruiker gewoon vragen voor een number, en dat number moet gedubbeld worden. Maar dat werkt niet, wat is er fout met mijn code?

Bedankt!

[ Voor 46% gewijzigd door ONiel op 10-07-2016 23:30 ]

Beste antwoord (via ONiel op 11-07-2016 12:55)


  • windwarrior
  • Registratie: Januari 2009
  • Laatst online: 12-07-2024

windwarrior

ʍıupʍɐɹɹıoɹ

Ik wil hier graag aan toevoegen wat hier gaande is. Ten eerste, mijn uitleg is niet perfect, ik probeer een balans te vinden tussen een didactisch verantwoord en een correct antwoord. De main functie binnen haskell is een beetje een bijzonder geval. De main functie in haskell leeft in een speciale context, IO. Dat wil zeggen dat je binnen de main functie toegang hebt tot IO specifieke functies zoals putStrLn. De functie summation die je geschreven hebt heeft als type Int -> Int -> Int, en leeft niet in deze IO context.

Een context zoals IO heet ook wel een Monad en levert haskell een manier op om sequentie en fouten te encoderen. Over monads zijn veel artikelen geschreven, maar de makkelijkste uitleg is die van LearnYouAHaskell. Verder wil ik je niet te veel bezwaren met monadisch programmeren.

Het do statement is een handige shortcut binnen Haskell om meerdere monadische operaties achter elkaar uit te voeren. Eigenlijk is een do-statement niets meer dan 'syntactic sugar' voor de twee monadische operatoren >> en >>=.

So far so good, nu geef ik een stukje foutieve do-notatie code en zal ik proberen uit te leggen waarom Haskell dit niet slikt. Monads kan je voor nu vergeten, wat volgt is een stukje types binnen Haskell en is een zeer nuttig concept.

Haskell:
1
2
3
do
    a <- getLine
    (read a :: Int)


Als we de do-notatie hiervan "desugaren", krijgen we het volgende. Dit stuk code is nogsteeds foutief.

Haskell:
1
getLine >>= \x -> (read x :: Int)


Verder geef ik de types van de verschillende delen van deze expressie, ik heb hier types ingevuld om je niet te ver in verwarring te brengen. De uitvoer hier komt uit GHCi, het commando :t kan je gebruiken om te achterhalen wat het type van een expressie is, zo levert :t (+) dus het type van de plus operatie op!
Haskell:
1
2
3
4
5
6
7
8
9
:t getLine
> getLine :: IO String

:t \x -> (read x :: Int)
> \x -> (read x :: Int) :: String -> Int

# Het volgende type is eigenlijk complexer, voor nu heb ik alle 'm's vervangen door IO en de uitvoer iets versimpelt.
:t (>>=)
>  (>>=) :: IO String -> (String -> IO Int) -> IO Int


Nu komt het interessante voor jou als beginnende Haskell programmeur. Zoals je kan zien is het eerste argument van >>= van het correcte type, Haskell verwacht een IO String en ontvangt deze ook. Het tweede argument van >>= is echter niet wat Haskell verwacht, haskell is op zoek naar een functie van een string naar een getal binnen het IO doosje, maar je geeft haskell een functie van string naar een getal (zonder doosje). Daartoe gaat de typecheck van haskell op zijn gezicht.

Uiteindelijk heb je het probleem opgelost door simpelweg putStrLn $ show toe te voegen aan het read statement, dit werkt omdat de functie putStrLn van het type String -> IO () is, en de funcie show in dit geval een getal omzet naar een string. Wat zegt dit type nou precies, de functie putStrLn neemt een String waarde in geen doosje, en levert een unit waarde op in het IO doosje. Dit typecheckt, en het type van de >>= wordt dus uiteindelijk van >>= :: String -> (String -> IO ()) -> IO ().

Voor de volledigheid is daarmee de correcte code:

Haskell:
1
2
3
do
    a <- getLine
    putStrLn $ show $ (read a :: Int)


Ik hoop dat hiermee duidelijk is geworden wat er daadwerkelijk fout was aan je code. Veel Haskell-plezier nog!

[ Voor 9% gewijzigd door windwarrior op 11-07-2016 03:45 ]

Alle reacties


Acties:
  • 0 Henk 'm!

  • Room42
  • Registratie: September 2001
  • Niet online
Come on! "Het werkt niet" bestaat niet, dat weet je best. Je krijgt vast een error of onverwachte output. Dat moet je kunnen benoemen.

Dus, vertel. Wat gaat er mis?

"Technological advancements don't feel fun anymore because of the motivations behind so many of them." Bron


Acties:
  • 0 Henk 'm!

  • ONiel
  • Registratie: September 2015
  • Laatst online: 15-06 21:16
Ohwja, sorry.
Compiler-output was ik vergeten.

Acties:
  • +1 Henk 'm!

  • Room42
  • Registratie: September 2001
  • Niet online
En zo dan? [google=Couldn't match expected type `IO a0' with actual type `Int']

Ik spreek zelf geen Haskell, maar er zijn blijkbaar honderden mensen met dezelfde melding. De eerste hit klinkt al redelijk plausibel: http://stackoverflow.com/...pe-io-a0-with-actual-type

"Technological advancements don't feel fun anymore because of the motivations behind so many of them." Bron


Acties:
  • 0 Henk 'm!

  • ONiel
  • Registratie: September 2015
  • Laatst online: 15-06 21:16
Dat heb ik al geprobeerd, en dit was de code:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import System.IO

prompt txt = do
    putStr txt
    hFlush stdout
    getLine

doDouble x = x * 2

main = do
    name <- prompt "Hi, what's ya name? "
    putStrLn ("Hello " ++ name)

    nmrToDouble <- prompt "Choose a number to double: "
    let doDouble = doDouble nmrToDouble
    persist doDouble


output:
* Occurs check: cannot construct the infinite type: t ~ String -> t
* In the expression: doDouble nmrToDouble
In an equation for `doDouble': doDouble = doDouble nmrToDouble
In the expression:
do { name <- prompt "Hi, what's ya name? ";
putStrLn ("Hello " ++ name);
nmrToDouble <- prompt "Choose a number to double: ";
let doDouble = doDouble nmrToDouble;
.... }
* Relevant bindings include
doDouble :: String -> t
(bound at C:\users\niel\desktop\test.hs:15:9)

C:\users\niel\desktop\test.hs:16:5: error:
Variable not in scope: persist :: (String -> t0) -> IO b
Het werkt niet, en ik snap niet hoe je Haskell kan leren als er enkel rotslechte documentaties/tutorials/boeken zijn waarvan de syntax bij allemaal verschillen, en ze allemaal twintig héél verschillende compilers gebruiken, en elk antwoord over Haskell de logica ver weg is.

Acties:
  • 0 Henk 'm!

  • Room42
  • Registratie: September 2001
  • Niet online
Waarom wil je Haskell leren dan? :)

"Technological advancements don't feel fun anymore because of the motivations behind so many of them." Bron


Acties:
  • 0 Henk 'm!

  • ONiel
  • Registratie: September 2015
  • Laatst online: 15-06 21:16
Omdat het moet.
Nu is het rottig irritant en frustrerend om te leren.

Maar over een weekje snap ik het misschien wel, en kan ik ook nieuwere en specialere dingen in Haskell maken.
+ Dat wat ik in Haskell leer (manier van denken bijvoorbeeld) zou ik ook in andere programmeertalen kunnen gebruiken.

Acties:
  • 0 Henk 'm!

  • ONiel
  • Registratie: September 2015
  • Laatst online: 15-06 21:16
Ik heb het zelf al gevonden. ('k Heb je toch een +1 gegeven voor het reageren). ;)

Code:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import System.IO

summation x y = x + y

prompt txt = do
    putStr txt
    hFlush stdout
    getLine

main = do
    name <- prompt "Name: "
    putStrLn $ "Please " ++ name ++ " Give the..."

    a <- prompt "First number: "
    b <- prompt "Second number: "

    putStrLn $ show $ summation (read a) (read b)


Ik denk dat ik de taal en het concept van functioneel programmeren al een beetje begin te zien.

Acties:
  • Beste antwoord
  • +2 Henk 'm!

  • windwarrior
  • Registratie: Januari 2009
  • Laatst online: 12-07-2024

windwarrior

ʍıupʍɐɹɹıoɹ

Ik wil hier graag aan toevoegen wat hier gaande is. Ten eerste, mijn uitleg is niet perfect, ik probeer een balans te vinden tussen een didactisch verantwoord en een correct antwoord. De main functie binnen haskell is een beetje een bijzonder geval. De main functie in haskell leeft in een speciale context, IO. Dat wil zeggen dat je binnen de main functie toegang hebt tot IO specifieke functies zoals putStrLn. De functie summation die je geschreven hebt heeft als type Int -> Int -> Int, en leeft niet in deze IO context.

Een context zoals IO heet ook wel een Monad en levert haskell een manier op om sequentie en fouten te encoderen. Over monads zijn veel artikelen geschreven, maar de makkelijkste uitleg is die van LearnYouAHaskell. Verder wil ik je niet te veel bezwaren met monadisch programmeren.

Het do statement is een handige shortcut binnen Haskell om meerdere monadische operaties achter elkaar uit te voeren. Eigenlijk is een do-statement niets meer dan 'syntactic sugar' voor de twee monadische operatoren >> en >>=.

So far so good, nu geef ik een stukje foutieve do-notatie code en zal ik proberen uit te leggen waarom Haskell dit niet slikt. Monads kan je voor nu vergeten, wat volgt is een stukje types binnen Haskell en is een zeer nuttig concept.

Haskell:
1
2
3
do
    a <- getLine
    (read a :: Int)


Als we de do-notatie hiervan "desugaren", krijgen we het volgende. Dit stuk code is nogsteeds foutief.

Haskell:
1
getLine >>= \x -> (read x :: Int)


Verder geef ik de types van de verschillende delen van deze expressie, ik heb hier types ingevuld om je niet te ver in verwarring te brengen. De uitvoer hier komt uit GHCi, het commando :t kan je gebruiken om te achterhalen wat het type van een expressie is, zo levert :t (+) dus het type van de plus operatie op!
Haskell:
1
2
3
4
5
6
7
8
9
:t getLine
> getLine :: IO String

:t \x -> (read x :: Int)
> \x -> (read x :: Int) :: String -> Int

# Het volgende type is eigenlijk complexer, voor nu heb ik alle 'm's vervangen door IO en de uitvoer iets versimpelt.
:t (>>=)
>  (>>=) :: IO String -> (String -> IO Int) -> IO Int


Nu komt het interessante voor jou als beginnende Haskell programmeur. Zoals je kan zien is het eerste argument van >>= van het correcte type, Haskell verwacht een IO String en ontvangt deze ook. Het tweede argument van >>= is echter niet wat Haskell verwacht, haskell is op zoek naar een functie van een string naar een getal binnen het IO doosje, maar je geeft haskell een functie van string naar een getal (zonder doosje). Daartoe gaat de typecheck van haskell op zijn gezicht.

Uiteindelijk heb je het probleem opgelost door simpelweg putStrLn $ show toe te voegen aan het read statement, dit werkt omdat de functie putStrLn van het type String -> IO () is, en de funcie show in dit geval een getal omzet naar een string. Wat zegt dit type nou precies, de functie putStrLn neemt een String waarde in geen doosje, en levert een unit waarde op in het IO doosje. Dit typecheckt, en het type van de >>= wordt dus uiteindelijk van >>= :: String -> (String -> IO ()) -> IO ().

Voor de volledigheid is daarmee de correcte code:

Haskell:
1
2
3
do
    a <- getLine
    putStrLn $ show $ (read a :: Int)


Ik hoop dat hiermee duidelijk is geworden wat er daadwerkelijk fout was aan je code. Veel Haskell-plezier nog!

[ Voor 9% gewijzigd door windwarrior op 11-07-2016 03:45 ]


Acties:
  • 0 Henk 'm!

  • ONiel
  • Registratie: September 2015
  • Laatst online: 15-06 21:16
Hartelijk bedankt voor de uitleg!
Dat maakte ook al wat meer duidelijk. :)

Alleen een vraagje,
je zegt dat main toegang heeft tot IO-functies. Maar andere functies toch ook? Zoals mijn prompt-functie.

Acties:
  • +1 Henk 'm!

  • windwarrior
  • Registratie: Januari 2009
  • Laatst online: 12-07-2024

windwarrior

ʍıupʍɐɹɹıoɹ

Correct, je prompt functie leeft ook in de IO context, dat kan je zien omdat het type van je prompt functie String -> IO String is. Dit kan je soort van lezen als, een functie van string naar een String die zich in het IO doosje bevind.

[ Voor 21% gewijzigd door windwarrior op 11-07-2016 13:03 ]


Acties:
  • 0 Henk 'm!

  • ONiel
  • Registratie: September 2015
  • Laatst online: 15-06 21:16
Ok, hartelijk bedankt!
Pagina: 1