Range Over Iterators in Idris

Our program will demonstrate how to range over iterators using Idris. Here’s the full source code.

module Main

import Data.List
import Control.Monad

data List a = MkList { head : Maybe (Element a) }

data Element a = MkElement { next : Maybe (Element a), val : a }

push : a -> List a -> List a
push v (MkList Nothing) = MkList (Just (MkElement Nothing v))
push v (MkList (Just tail)) = MkList (Just (MkElement (Just (MkElement Nothing v)) (val tail)))

-- All returns a Stream, which is a special structure in Idris
allElements : List a -> Stream a
allElements (MkList Nothing) = repeat []
allElements (MkList (Just el)) = iterate (\e => fmap next e) (Just el) >>= fmap val

-- Function returning a Stream of Fibonacci numbers
genFib : Stream Int
genFib = let fibs = 1 :: 1 :: zipWith (+) fibs (tail fibs) in streamFromList fibs

main : IO ()
main = do
    let lst = push 23 $ push 13 $ push 10 $ MkList Nothing
    putStrLn "List Elements:"
    sequence_ $ mapM_ (putStrLn . show) (streamTake 3 (allElements lst))

    putStrLn "Fibonacci Numbers:"
    sequence_ $ mapM_ (putStrLn . show) (streamTakeWhile (< 10) genFib)

Explanation

List and Element Definitions

We define a List as a structure that may contain a head, which is an Element. The Element itself contains a value and may point to the next Element.

Push Function

The push function adds a new element to the list. If the list is empty it creates a new element, else it adds it as the next element of the last one.

AllElements Function

allElements returns a Stream of values in the List, where Stream is an Idris construct suited for lazy evaluation. We use iterate to traverse through the elements, and fmap to collect the values.

Fibonacci Generator

The genFib function generates an infinite Stream of Fibonacci numbers, utilizing Idris’s ability to handle infinite structures through lazy evaluation.

Main Function

In the main function, we:

  1. Create a list and add elements to it.
  2. Print the elements in the list.
  3. Print Fibonacci numbers less than 10.

By using sequence_, we ensure that the IO operations (printing in this case) are executed in sequence.