9

Being very new to Haskell, I'm wondering how to 1) compute something until a certain criterion is satisfied, and then 2) return the computed value.

In the languages I know, you would use a while loop for that. How do you do it in Haskell?

jub0bs
  • 60,866
  • 25
  • 183
  • 186
Myke
  • 281
  • 1
  • 2
  • 12
  • 2
    Haskell uses recursion in place of loops. – jub0bs Dec 10 '14 at 14:59
  • 2
    @Jubobs: I do not think you can say that for the general case, as mapping, folding and filtering are also used in place of loops. Apart of course from even more other functions and even libraries of functions. It always depends; I would say, on the first level of tool lineup, it's map, filter, fold and recursion. – Sebastian Mach Dec 10 '14 at 15:14
  • @phresnel Haskell's `map`, `filter` and folds are all implemented as recursive functions... Those functions just add a layer of abstraction on top of recursion, but you always find recursion at the bottom of it all. – jub0bs Dec 10 '14 at 15:16
  • 1
    @Jubobs: True, and I do not deny that. But e.g. lambdas in C++ are implemented as classes with a function call operator; yet you wouldn't advice on using _classes with function call operator_ where a lambda (as a tool) is more appropriate. Would you say that Haskell's map et al are less idiomatic and/or advisable than "raw" recursion? – Sebastian Mach Dec 10 '14 at 15:21
  • @phresnel No. You should use `map`, `filter`, `foldr` etc. whenever appropriate, but I'm saying that, at the bottom of it, recursion rules them all. – jub0bs Dec 10 '14 at 15:26
  • @Jubobs: Of course and true; yet I would prefer enumerating them in one sequence with recursion when stating alternatives to loops; both for pedagogic and idiomacy reasons (to open Pandora's box: at the bottom of all, Python interpreters often make use of pointers). Anyways, I realise this is more a preference thing :) – Sebastian Mach Dec 10 '14 at 15:30
  • @Jubobs Actually, that's not *necessarily* true. If you use a different (and non-recursive) representation of lists, you can write non-recursive implementations of `map` and `foldr`: http://www.haskellforall.com/2014/09/morte-intermediate-language-for-super.html#recursion – David Young Dec 10 '14 at 17:03

4 Answers4

26

You should use recursion:

func :: <function type>
func <arguments> = 
    if condition 
        then <recursive call>
        else computedValue

There are also other utilities you'll discover in the future, such as until, that will help you with this. In the end it really depends on the semantic of the loop and condition. For example if the condition is simply "until we reach the end of a list" you can simply use map or one of the fold-family functions.

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • I wanted to return a list in the else statement when the condition is satisfied. For example, `fun [] = [] fun (x:xs) = if x == 0 then x:fun xs else ...' for instance I gave the list [1,2,0,3,4] so it should return [1,2]. – Myke Dec 10 '14 at 15:13
  • 2
    @Myke If you give some `while` loop in some language as an example, I might show you what would be the equivalent in Haskell. – Shoe Dec 10 '14 at 15:15
  • For example, `fun [] = [] fun (x:xs) = if x == 0 then x:fun xs else ...' for instance I gave the list [1,2,0,3,4] so it should return [1,2] – Myke Dec 10 '14 at 15:19
  • 1
    @Myke Try `fun = takeWhile (/= 0)`. – jub0bs Dec 10 '14 at 15:20
  • No.. I'm not exactly implementing the above example.. but it's similar analogy – Myke Dec 10 '14 at 15:21
  • any suggestion on making use of if ... else ... like takeWhile? – Myke Dec 10 '14 at 15:23
  • @Myke `myTakeWhile p [] = []`; `myTakeWhile p (x:xs) = if p x then x : myTakeWhile p xs else []`. This is approximately the [definition for the built in `takeWhile`](http://hackage.haskell.org/package/base-4.7.0.1/docs/src/GHC-List.html#takeWhile). – bheklilr Dec 10 '14 at 15:39
11

The answer is recursion. To give a pedantic example:

In Python:

def fib(n):
    a = 0
    b = 1
    while n > 0:
        a, b = b, a + b
        n -= 1
    return b

In Haskell:

fib 0 = 1
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

Or equivalently without pattern matching

fib n = if n == 0 || n == 1 then 1 else fib (n - 1) + fib (n - 2)

Or more efficiently

-- the local variable fibs is an infinite list of all Fibonacci numbers
fib n = fibs !! n where fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

If you want to do something like read text from STDIN until you get a line that reads q, then the easiest way is something like

import Control.Monad (unless)

prompt :: IO ()
prompt = do
    -- get input from user
    l <- getLine
    -- unless will execute its block if the condition is False
    unless (l == "q") $ do
        -- echo back to the user
        putStrLn $ "You entered: " ++ l
        prompt  -- recursive step here

Or in Python

def prompt():
    l = input()
    while l != "q":
        # Assuming Python 3
        print("You entered: " + l)
        l = input()
bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • 4
    Might be worth mentioning why the first and second Haskell versions of `fib` is a bad idea. – jub0bs Dec 10 '14 at 15:22
  • 1
    Some useful looping contol structures are also defined in [Control.Monad.Loops](http://hackage.haskell.org/package/monad-loops-0.4.3/docs/Control-Monad-Loops.html). – Anderson Green Jun 27 '19 at 14:43
10

Haskell does not have an intrinsic equivalent of while loops based on mutable state.

Instead, you typically

  • use map family of functions over a range of values to produce a new range of values
  • use filter family of functions over a range of values to produce a new subset of that range, with certain conditions fulfilled
  • use fold family of functions to aggregate something over that range
  • use recursion to do whatever you want (except mutating input, of course).

.

Of course, Haskell and libraries provide functions to make your life easier, but while while loops can be considered idiomatic/"first class" in imperative languages, what is idiomatic/"first class" in Haskell (and other functional programming languages) is recursion, mapping, filtering and folding.

Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
  • Doesn't really answer the question. You don't use map, filter or fold to do what the question asks. – JacquesB Dec 11 '14 at 10:38
  • @JacquesB: By _"how to compute something"_ I cannot exclude that map, filter or fold should be used. Your fortune telling is great. – Sebastian Mach Dec 11 '14 at 10:52
0

Facing the same problem that I need to convert C to Haskell. The key concepts are the conditions and the built-in if-then-else statements.

Say there's a program in C

int auto_drive (int speed) {
  int speed_limit = 90;
  while (speed > 0 & speed != speed_limit) {
    if (speed > speed_limit) {
      speed = speed - 1;
    } else {
      speed = speed + 1;
    }        
  }
  return speed;
}

Convert it to Haskell:

auto_drive :: Int -> Int
auto_drive speed = mywhile (90,speed)

mywhile x =
  if condition x then mywhile (next_version_of x) -- if condition met, pass x to the function next_verion_of, next, recursion back to mywhile function 
  else final_version_of x -- otherwise, it would have the final speed

condition (speed_limit,speed) = speed > 0 && speed /= speed_limit 

next_version_of (speed_limit,speed) =
  if speed > speed_limit then (speed_limit,speed-1) else (speed_limit,speed+1) -- converge the speed to the speed limit

final_version_of (speed_limit,speed) = speed
Woden
  • 1,054
  • 2
  • 13
  • 26