Reputation: 65
i am trying to compile a quite simple haskell program. Either I don't know how to sequence statements (expressions) in Haskell, or there isn't a way to do it.
I essentially want a function of the form:
f = <do stuff 1>
<do stuff 2>
...
the following was supposed to simulate a pattern of
<do stuff>
<tail recursion>
but failed to compile:
go 0 = 0
go i = case i of
1 -> 2
_ -> 3
go (i-1)
This doesn't work either (simpler, no recursion):
go i = 1
2
The code directly above compiles, but while running I get the cryptic:
No instance for (Num (a0 -> t0))
arising from a use of `go'
Possible fix: add an instance declaration for (Num (a0 -> t0))
In the expression: go 2
In an equation for `it': it = go 2
Am I having a problem with indention? A problem with not sequencing correctly? Or is there no sequencing capability? If no sequencing, how would you straightforwardly do
<do stuff>
<tail recursion>
?
Thanks for the responses.
Hard to believe a function in Haskell is basically limited to one "expression" or "statement" or whatever you want to call it. How would you solve the following contrived toy example (written in erlang, but could easily be translated direcly into prolog or a whole host of other langs):
count([], X) -> X;
count([_|B], X) ->
Y = X+1,
count(B, Y).
An example run:
erlang> count([a,b,c,d,e],0).
5
The 2nd clause fits the pattern I described earlier of:
<do stuff>
<tail recursion>
I guess this particular example maps onto the Haskell "let... in" someone described here. But what if there needs to be 10 "do stuffs", or 20, or more? And is this "let... in" guaranteed to be tail recursive (the above example is guaranteed to be in erlang, prolog, scheme, etc).
Is there a way I can simply "string together" or "sequence" a bunch of statements or expressions? In prolog or erlang the sequencing is accomplished by the comma (see toy example above). In c/c++ it is accomplished by the semicolon. I thought the Haskell operator was simply whitespace, but perhaps it just isn't done in this language?
Upvotes: 3
Views: 3680
Reputation: 3511
A function mathematically is defined by its unique map from input to output. A sequence is created by forwarding output to a new function. So the natural way to do sequences in Haskell is function composition ... f2 . f1
. Sometimes a function returns more than what is needed for the next step. So you need let
or where
to extract what you want to forward to the next step.
forward only refers to from output to input. From input to output it is a map between different variables (a function).
Nothing is ever changed. No side effect.
do
notation is also function composition. You might think something (Monad) is handled through the functions and possibly changed, but it is not changed. Instead of a change a modified copy is constructed. Well, ideally, because IO
-like Monads will do changes to the outside world.
Upvotes: 0
Reputation: 139431
Update: Be careful that you’re not writing Scheme programs in Haskell. Accumulating parameters are rare in idiomatic Haskell, which uses laziness rather than tail recursion.
For example, the definition of count
in Haskell will resemble
count [] = 0
count (x:xs) = 1 + count xs
You might object that the source of length
looks more Scheme-ish
-- | /O(n)/. 'length' returns the length of a finite list as an 'Int'.
-- It is an instance of the more general 'Data.List.genericLength',
-- the result type of which may be any kind of number.
length :: [a] -> Int
length l = len l 0#
where
len :: [a] -> Int# -> Int
len [] a# = I# a#
len (_:xs) a# = len xs (a# +# 1#)
but this is low-level, non-idiomatic code. The referenced genericLength
has the same structure as count
above and has broader applicability when compared to length
.
genericLength :: (Num i) => [b] -> i
genericLength [] = 0
genericLength (_:l) = 1 + genericLength l
“Do this and then do that” in Haskell is expressed as function composition in pure code. For example, to compute the length of the longest sublist, we’d compute the sublists’ lengths and then the maximum over those lengths. In Haskell, that’s expressed
Prelude> :t maximum . map length
maximum . map length :: [[a]] -> Int
or
Prelude> :m + Data.List
Prelude Data.List> :t maximum . map genericLength
maximum . map genericLength :: (Ord c, Num c) => [[b]] -> c
Note: laziness adds nuance, but the general point holds.
Even in “imperative” code inside IO
such as
main :: IO ()
main = do
putStr "Hello "
purStrLn "world!"
is function composition under the covers because it has the same semantics as
main :: IO ()
main = putStr "Hello " >>= \_ -> putStrLn "world!"
Perhaps an example that uses the list monad will make this clearer. Say we want all Pythagorean triples (a,b,c)
such that no component is greater than some maximum n.
import Control.Monad (guard)
triples :: Integer -> [(Integer,Integer,Integer)]
triples n = do
a <- [1 .. n]
b <- [a .. n]
c <- [b .. n]
guard $ a*a + b*b == c*c
return (a,b,c)
As you can see, we must first choose a candidate value of a
, then of b
, and so on.
The compiler transforms this code to explicitly use >>=
, the monadic bind combinator.
-- indented to show nested scopes
triples_bind :: Integer -> [(Integer,Integer,Integer)]
triples_bind n =
[1 .. n] >>= \a ->
[a .. n] >>= \b ->
[b .. n] >>= \c ->
(guard $ a*a + b*b == c*c) >>= \_ ->
return (a,b,c)
Inside the list monad, >>=
is concatMap
, so the above is identical to
triples_stacked :: Integer -> [(Integer,Integer,Integer)]
triples_stacked n =
concatMap (\a ->
concatMap (\b ->
concatMap (\c ->
concatMap (\_ ->
[(a,b,c)])
(guard $ a*a + b*b == c*c))
[b .. n])
[a .. n])
[1 .. n]
The indentation shows structure and is pleasing because it gives the impression of the Haskell logo that combines λ with >>=
.
Yet another way to express the same semantics is
triples_cm n = concatMap step2 [1 .. n] -- step 1
where step2 a = concatMap step3 [a .. n]
where step3 b = concatMap step4 [b .. n]
where step4 c = concatMap step5 (guard $ a*a + b*b == c*c)
where step5 _ = [(a,b,c)]
Haskell’s pattern matching already does case
matching behind the scenes. Instead of
go 0 = 0
go i = case i of
1 -> 2
_ -> 3
go (i-1)
finish the pattern you started.
go 0 = 0
go 1 = go (2 - 1)
go _ = go (3 - 1)
Remember that variables in Haskell are immutable: they do not have destructive update as in C or Java. You get one shot at defining a variable’s value, and then you’re stuck with it. You might instead define go
as
go 0 = 0
go x = let i = case x of 1 -> 2
_ -> 3
in go (i - 1)
or
go 0 = 0
go x | x == 1 = go (2 - 1)
| otherwise = go (3 - 1)
The examples above all typecheck but have the same big problem, namely that go
terminates only when its argument is zero. You didn’t provide enough information for us to help with that problem. Tell us what it is you’re trying to do, and we can offer specific advice for your situation.
Upvotes: 1
Reputation: 12077
It is very good that you were able to provide an Erlang code example of what you mean by 'doing stuff'. Until now it's been a little vague. So long as the discussion is only about strict languages, it's okay to be somewhat vague on that point since the rules are fairly uniform and understood.
On the other hand, we are talking about Haskell, a language with 'lazy' evaluation, so it is good to be very specific about what 'doing stuff' means, otherwise there is likely to be confusion due to incorrect assumptions.
Here's a fairly direct translation of the Erlang function you provided in Haskell:
count ([],x) = x
count (_:b, x) =
let y = x + 1
in count (b, y)
As you can see, it's pretty much identical in structure to the Erlang version. As such, you may be wondering what is the big deal? If it's that easy to 'do stuff' in Haskell, then why is everyone making it sound so complicated?
One way to explain what is the big deal is to point out that the above Haskell function is equivalent to this one:
count ([],x) = x
count (_:b, x) = count (b, x + 1)
Also this one:
count ([],x) = x
count (_:b, x) = count (b, y) where y = x + 1
This is because the order in which Haskell evaluates expressions does not depend on the lexical order of those expressions. In general, Haskell will delay expression evaluation to the last possible moment.
Now when you think about composing this scheme of lazy evaluation together with side-effects (reading or writing files, sending output to the screen, etc.), it gets a little fuzzy. The order in which side-effects happen is generally very important. Fortunately, functions in Haskell that deal with side-effects generally do not perform the side-effecting action themselves. Rather they produce an 'action' value that describes the side-effect. Then there are other functions that can compose these 'action' values in a way that respects order. Then you just need something that will actually perform the side-effects that are described by the 'action' values...
Upvotes: 5
Reputation: 53665
In Haskell, you don't write functions that "do stuff". You write functions that compute a value. In that light, it makes no sense to structure a function in the way you suggest:
f = <do stuff 1>
<do stuff 2>
Instead, you write an actual, math-y function, which is a single expression dependent on certain inputs:
f x y = blah x blah y blah
It might be convenient to break it up into sub-expressions
f x y = res1 + res2
where res1 = x * 3
res2 = y + 4
But in the end, it is just a single expression.
Monadic code written in do
notation can appear to "do stuff" sequentially:
f = do
thing1
thing2
But this is just an illusion. It desugars into a single expression as well:
f = thing1 >> thing2
This expression states that f
is a value which is the composition of thing1
and thing2
. Now, IO code might compile to code that "does stuff" sequentially, but that's not what it means in Haskell. One does not simply "do stuff" in Haskell. Instead, you compose.
Upvotes: 25
Reputation: 612
I'm not sure got correctly what your function has to do. May be you need something like this:
go :: Int -> Int
go 0 = 0
go i = go (i-1)
if that function takes argument e.g. '3' it calculates go 3 -> go 2 -> go 1 -> go 0 -> 0 and get answer '0'
Now if you want some sequencing you can use keyword 'do', but I am sure it is now what you need. If you are trying to create some kind of 'variables' look at 'let' & 'in' statements :
someInput = 7
gogogo 0 = 0
gogogo n =
let
gavk = n + 1
kravk = gavk * 2
out = kravk + 1
in
out
main =
do
putStrLn "this is sample do statement"
putStrLn "gogogo:"
putStrLn $ gogogo someInput
UPD. edited mistypes
Upvotes: 0
Reputation: 47042
When you say "do stuff", what do you mean?
In the first case, use let
...in
:
quadraticRoots a b c = let discriminant = b * b - 4 * a * c
root = sqrt discriminant
in case compare discriminant 0 of
LT -> []
EQ -> [-b / (2 * a)]
GT -> [(-b + root) / (2 * a), (-b - root) / (2 * a)]
In the second case, use do
notation:
main = do args <- getArgs
case map read args of
[a, b, c] -> putStrLn $ case quadraticRoots a b c of
[] -> "no real roots"
[x] -> "single root: " ++ show x
[x1, x2] -> unwords ["two real roots:", show x1,
"and", show x2]
_ -> hPutStrLn stderr "Please provide three coefficients on the command line"
Upvotes: 1
Reputation: 92966
Basically, there is no way to do it. Haskell is a purely functional language, there is neither change nor order of statements. One way to simulate sequence are monads. There are plenty of tutorials in the internet, I personally advise you to read the monad-chapters of Learn You A Haskell For Great Good! or Real World Haskell. Another thing is: Sequential operations are not very idiomatic in Haskell. You should better start with something different.
Upvotes: 2