Sequence detection in Haskell?

I am trying to compile a fairly simple haskell program. Either I don’t know how to arrange expressions (expressions) in Haskell, or there’s no way to do it.

I need a function like:

f = <do stuff 1>
    <do stuff 2>
    ...

      

supposed to simulate a template

<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 (easier, no recursion):

go i =  1
        2

      

The code is directly above compilation, but while working I get cryptic information:

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

      

I have a problem with indentation? Incorrect sequence problem? Or is there no sequencing capability? If there is no consistency, how would you directly do

<do stuff>
<tail recursion>

      

?


Thanks for answers.

It's hard to believe that a function in Haskell is mostly 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 into prologue or a whole host of other languages):

count([], X) -> X;
count([_|B], X) ->
    Y = X+1,
    count(B, Y).

      

Example:

erlang> count([a,b,c,d,e],0).
5

      

The second sentence follows the pattern I described earlier:

<do stuff>
<tail recursion>

      

I'm assuming this particular example maps to Haskell the "let ... in" described here. But what if there should be 10 "doing things", or 20 or more? And this "let ... in" is guaranteed to be a tail recursive (the given example is guaranteed to be in erlang, prologue, schema, etc.).

Is there a way that I can just "group" or "order" a bunch of statements or expressions? In prologue or erlang, the sequence is executed with a comma (see the toy example above). In c / C ++, this is achieved with a semicolon. I thought the Haskell operator was just whitespace, but maybe it just isn't done in this language?

+3


source to share


6 answers


Mandatory mei

In Haskell, you don't write functions that "do things". You write functions that compute a value. In this light, it makes no sense to structure the function the way you suggest:

f = <do stuff 1>
    <do stuff 2>

      

Instead, you write the actual math-y function, which is the only expression that depends on certain inputs:

f x y = blah x blah y blah

      

It might be convenient to break it down into subexpressions

f x y = res1 + res2
  where res1 = x * 3
        res2 = y + 4

      



But in the end, it's just one expression.


Monodeficient code written in a note do

may appear to "do things" sequentially:

f = do
  thing1
  thing2

      

But this is just an illusion. It also contains one expression:

f = thing1 >> thing2

      

This expression indicates that f

is a value that is a composition thing1

and thing2

. Now IO code can compile code that "makes stuff" consistently, but that's not what it means in Haskell. In Haskell, it's not just "doing things". Instead, you compose.

+20


source


It's great that you were able to provide some sample Erlang code for what you mean by "doing things". So far, this has been a little vague. While we are talking only about strict languages, in this case one can be somewhat vague, since the rules are quite uniform and understandable.

On the other hand, we are talking about Haskell, a lazy-evaluated language, so it is good to have an idea of ​​what it means to "do things", otherwise there can be confusion due to incorrect assumptions.

Here's a straightforward 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 is almost identical in structure to the Erlang version. So you might be wondering what's the big deal? If it's so easy to "do something" in Haskell, then why is everyone making it sound so complicated?

One way to explain what a big deal is is to point out that the above Haskell function is equivalent to this:



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 expressions are evaluated is independent of the lexical order of those expressions. In general, Haskell will delay expression evaluation until the last moment.

Now when you think about putting together this lazy evaluation scheme along with side effects (reading or writing files, sending output to the screen, etc.), it gets a little fuzzy. The order in which side effects occur is usually very important. Fortunately, functions in Haskell that deal with side effects don't do side effects themselves at all. Rather, they create an "action" meaning that describes a side effect. Then there are other functions that can compose these "action" values ​​in such a way that the order is respected. Then you just need something that will actually perform the side effects described by the "action" values ​​...

+5


source


Basically, there is no way to do this. Haskell is a purely functional language, there is no change or order of statements. Monads are one way to mimic a sequence. There are many tutorials on the internet, I personally advise you to read the chapters of the "Teach You" monad Haskell For Great Good! or Real World Haskell . Another thing: sequential operations are not very idiomatic in Haskell. You'd better start somewhere else.

+2


source


When you say "do things" what do you mean?

  • compute something, transform the data that we already have?
  • interact with the user, read / write a file, download something from the internet?

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 the notation do

:

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"

      

+1


source


Update: Be careful not to write Scheme programs in Haskell. Cumulative parameters are rare in idiomatic Haskell, which uses laziness rather than tail recursion.

For example, a definition count

in Haskell would resemble

count [] = 0
count (x:xs) = 1 + count xs

      

You might argue that the source length

looks more like 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 link genericLength

has the same structure as count

above and has wider applicability compared to length

.

genericLength           :: (Num i) => [b] -> i
genericLength []        =  0
genericLength (_:l)     =  1 + genericLength l

      

"Do it, then do it" in Haskell is expressed as a compound function in clean code. For example, to compute the length of the longest sublist, compute the lengths of the subscriptions, and then the maximum over those lengths. In Haskell, this is 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 common point is there.

Even in "imperative" code inside IO

, for example

main :: IO ()
main = do
  putStr "Hello "
  purStrLn "world!"

      

is a composition of functions under the covers as it has the same semantics as

main :: IO ()
main = putStr "Hello " >>= \_ -> putStrLn "world!"

      

Perhaps an example that uses the list monad would make this clearer. Let's say we want all Pythagorean triples to be (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 have to select the candidate value first a

, then b

, etc.

The compiler converts 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 monad of the list >>=

is concatMap

, so the above matches

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 nice because it gives the impression of a Haskell logo that combines λ with >>=

.

Another way to express the same semantics:

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)]

      


Haskells pattern matching is already doing the case

matching behind the scenes. Instead

go 0 = 0
go i =  case i of
            1 -> 2
            _ -> 3
        go (i-1)

      

complete the started template.

go 0 = 0
go 1 = go (2 - 1)
go _ = go (3 - 1)

      

Remember that variables in Haskell are immutable: they are not destructively updated like in C or Java. You get one chance to figure out the value of the variables, and then you get stuck with it. Instead, you can 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 it go

only completes when its argument is zero. You have not provided enough information for us to help with this issue. Tell us what you are trying to do and we can offer specific guidance for your situation.

+1


source


I'm not sure what is correct what your function should do. Maybe you want something like this:

go :: Int -> Int
go 0 = 0
go i = go (i-1)

      

if this function takes an argument, eg. '3' he calculates go 3 → go 2 → go 1 → go 0 → 0 and gets the answer "0"

Now, if you want to execute a sequence, you can use the 'do' keyword, but I'm sure this is what you need now. If you are trying to create any "variables", look at the "let" and "in" expressions:

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 errors

0


source







All Articles