Last Updated: 2018-12-11 Tue 18:12

CSCI 2041 Lab13: Lazy and Stream Modules

CODE DISTRIBUTION: lab13-code.zip

  • Download the code distribution every lab
  • See further setup instructions below

CHANGELOG: Empty

1 Rationale

Most programming environments feature eager evaluation of function arguments. However, there is an alternative philosophy of "lazy" evaluation which is provided in some environments, most significantly in the Haskell language. As a close cousin, OCaml supports lazy evaluation using the lazy keyword and functions in the Lazy module though it support is much more isolated than the lazy world view taken by Haskell. This lab briefly covers some lazy evaluation capabilities in OCaml.

Data often comes in "stream" form and OCaml provides a limited formalization of this via its Stream module. This allows arbitrary sources of data to be converted to a stream form which provides a simple, uniform interface for use in processing algorithms. This is a common software engineering technique, to provide a unifying access interface to very different data sources which is demonstrated in this lab.

Associated Reading / Preparation

Grading Policy

  • Check-off 30%: Demonstrate to a TA that a student understands answers to questions. This must be done in person in groups of one or two. Check-offs can happen during the lab period of during a TA office hour.
  • Submit 70%: Submit required files according to the lab instruction. This can be done at any time and from anywhere with a network connection. Submitting does not require attending lab. All students must submit files even if they were checked off in a group during lab.

See the full policy in the syllabus.

2 Codepack

The codepack for the lab contains the following files:

File State Description
QUESTIONS.txt Edit Answer questions in the file to complete the lab
lazy_calls.ml Analyze Problem 1 file to analyze
stream_demo.ml Analyze Problem 2 file to analyze
custom_lazy.ml Analyze Optional Problem file to analyze

3 Questions

Analyze the files in the provided codepack and answer the questions given in QUESTIONS.txt.

                           __________________

                            LAB 13 QUESTIONS
                           __________________


- Name: (FILL THIS in)
- NetID: (THE kauf0095 IN kauf0095@umn.edu)

Answer the questions below according to the lab specification. Write
your answers directly in this text file and submit it to complete the
lab.


PROBLEM 1: `lazy_calls.ml'
==========================

  Compile the file `lazy_calls.ml' and run it with several command line
  parameters such as
  ,----
  | > ocamlc lazy_calls.ml
  | > ./a.out 20
  | ...
  | > ./a.out 25
  | ...
  | > ./a.out 30
  | ...
  | > ./a.out 32
  | ...
  `----

  The input parameter is passed to a `fib n' function so will repeatedly
  compute the `nth' Fibonacci number. The questions below pertain to
  timing repeated calls to these functions so pick an `n' that is large
  enough to see timing info printed but not so large as to make repeated
  runs tediously long.


(A)
~~~

  Examine the timing information output for the section marked
  ,----
  | =====(1) either_args=====
  `----
  and find the corresponding block of code.

  Answer the following questions.
  1. Describe what the function `either_args' does, especially its
     dependence on the `(flag1,flag2)' parameters.
  2. Explain why the times printed for each invocation of `either_args'
     is identical regardless of the flags passed in. Incorporate the
     notion of *eager evaluation* in your answer.


(B)
~~~

  Examine the output with section marked
  ,----
  | =====(2) either_susp, new suspension each iter=====
  `----
  and locate the corresponding code in `lazy_calls.ml'.

  Contrast this with the first section by answering the following
  questions.
  1. How are `either_susp' and `either_arg' from the previous section
     different?
  2. Why are the times for some of the calls of `either_susp' different
     from each other?
  3. Explain why some of the times in section (1) and (2) differ and
     some are the same.


(C)
~~~

  Examine the output with section marked
  ,----
  | =====(3) either_lazy, lazy expr each iteration======
  `----
  and locate the corresponding code in `lazy_calls.ml'.

  1. How does the code associated with the `either_lazy' differ from
     `either_susp'? What module and special syntax are used?
  2. Compare the timings from output section (2) and (3) to each other
     and explain any similarities or differences.


(D)
~~~

  Examine the output with section marked `====(4) either_susp, single
  suspension before loop====' and locate the corresponding code in
  `lazy_calls.ml'.

  Contrast the code associated with (4) and the previous use of
  suspensions in (2): how do the codes differ? Explain why their timings
  are the same or why there are differences.


(E)
~~~

  Examine the output with section marked
  ,----
  | =====(5) either_lazy, new lazy expr each iter======
  `----
  and locate the corresponding code in `lazy_calls.ml'.

  Contrast the code associated with (5) and the previous use of
  suspensions in (3): how do the codes differ? Explain why their timings
  are the same or why there are differences.


PROBLEM 2: Streams of Data
==========================

  Compile and run the code in `stream_demo.ml'. Examine the results
  noting that you *must press Ctrl-c to kill the program* as it will
  otherwise loop infinitely.


(A)
~~~

  Analyze the first section of code associated with the heading
  ,----
  | =====finite stream from list=====
  `----

  Answer the following questions about the code.
  1. Describe what data source is used as a source for the stream and
     what library function is used to create a stateful stream from it.
  2. What library functions are used to access elements of the stream?
     Examine their documentation and describe what they do.
  3. Why does the output of the program differed between the list and
     stream from one iteration to the next?


(B)
~~~

  Examine the second section of `stream_demo.ml' with heading
  ,----
  | =====infinite int stream from function=====
  `----

  Answer the following questions about the code.
  1. As in the previous problem, describe what data source is used as a
     source for the stream and what library function is used to create a
     stateful stream from it.
  2. What library functions are used to access elements of the stream?
     Are they the same or different from part (A) above?
  3. Will the stream in this case ever "run out" of data? Explain your
     reasons why or why not?
  4. Explain why this stream can produce a lot of data but requires very
     little memory to represent.


Optional Extras
===============

Custom implementations of `lazy' fail
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  A first instinct for most folks observing the connection between
  Problem 1's suspensions via functions and the `lazy' keyword is to
  attempt their own implementation of it along the following lines.  (*
  custom_lazy.ml: optional problem exploring possible implementations of
  lazy expressions. *)


  type 'a lazy_expr = { (* type for lazy expressions *) expr : unit ->
      'a; (* expression to evaluate *) mutable result : 'a option; (*
      saved results, None if uneval'd yet *) };;

  let my_lazy expr = (* create a lazy version of expr *) {expr = (fun ()
    -> expr); result = None} ;;

  let my_force lazy_expr = (* force a value out of the lazy_expr *)
    match lazy_expr.result with
   Some a -> a                     (* already evaluated *) 
   None ->                         (* not eval'd yet *)    
       let a = lazy_expr.expr () in (* eval *) lazy_expr.result <- Some
       a; (* save result *) a (* return result *) ;;

  Experiment with this version by adding calls to its `my_lazy' into the
  demo in `lazy_calls.ml'.  See if it in fact creates the same effects
  as the built-in `lazy' keyword.

  When you determine that it does not, speculate as to why it fails and
  why compiler support is required for features like `lazy'.


Stream peek vs. has_more
~~~~~~~~~~~~~~~~~~~~~~~~

  It is natural to expect the `Stream' module to contain a function like
  `Stream.has_more stream' which returns true when the stream has more
  data and false otherwise. However, there is no such `has_more'
  function present and the `peek' function which returns a None/Some is
  used instead.  Why would it be difficult or even impossible to write a
  general purpose `has_more' function? Consider that streams are very
  general and in the sample code provided, data from two different
  sources is converted to streams.

4 What to Understand

Ensure that you understand

  • How the lazy keyword affects execution of an expression vs its usual eager evaluation
  • How lazy evaluation can affect program run times when expression results are not needed
  • Basics of stream processing functionality in the Stream module

5 Getting Credit: Submit and Check-off

  • Check-off your answers with a TA in person during lab or office hours.
  • Submit your completed QUESTIONS.txt file to Canvas under the appropriate lab heading. Make sure to paste in any new code and answers you were to write at appropriate locations in the file.

Author: Chris Kauffman (kauffman@umn.edu)
Date: 2018-12-11 Tue 18:12