Implementing Global Variables in Functional Programming Hrmel - - PDF document

implementing global variables in functional programming
SMART_READER_LITE
LIVE PREVIEW

Implementing Global Variables in Functional Programming Hrmel - - PDF document

1 Implementing Global Variables in Functional Programming Hrmel Nestra Institute of Computer Science University of Tartu e-mail: harmel.nestra@ut.ee 2 Outline Introduction of the problem. Five approaches to solve the problem.


slide-1
SLIDE 1

1

Implementing Global Variables in Functional Programming

Härmel Nestra

Institute of Computer Science University of Tartu e-mail: harmel.nestra@ut.ee

slide-2
SLIDE 2

2

Outline

  • Introduction of the problem.
  • Five approaches to solve the problem.

– Code show. – The series of the first 4 approaches follows the paper John Hughes, Global Variables in Haskell. JFP 14(5) (2004), pp 489–502. – The last approach to present, as well as the way of implementing variables via type families (and in fact, all code examples), is my contribution.

slide-3
SLIDE 3

1 The Problem

3

The Problem

slide-4
SLIDE 4

1 The Problem 1.1 Preliminaries from Functional Programming

4

Preliminaries from Functional Programming

slide-5
SLIDE 5

1 The Problem 1.1 Preliminaries from Functional Programming

5

Referential transparency

One of the main features of functional programming is referential transparency that means that – any subexpression affects the value of the whole expression only via its value; – i.e., any subexpression can be replaced with another with the same value; – i.e., the evaluation of any expression has no side-effects. Side-effects are changes in computation state that could have impact to the values of expressions.

slide-6
SLIDE 6

1 The Problem 1.1 Preliminaries from Functional Programming

6

Consequences

– There can be no side-effects at all (as the whole program is also an expression that must be evaluated). – The value of a variable does not change during reading its scope (otherwise, it could be used for creating side-effects). In mathematics analogously, correct interpretation of formula x2 = x · x assumes that the value of x is the same over the formula, although during different readings, the value of x can be different.

slide-7
SLIDE 7

1 The Problem 1.1 Preliminaries from Functional Programming

7

Input from the environment

In practice, it is inevitable to have programs that take information from the environment into account: – in interactive processes; – in handling asynchronous exceptions; – in using memory common to many threads; – etc..

slide-8
SLIDE 8

1 The Problem 1.1 Preliminaries from Functional Programming

8

A former problem, related to that of this talk

How to use the information taken from the environment while prevent- ing it from becoming a side-effect? – Solution (idea): assure that the values being influenced by the environment are always immediately assigned to variables at their initialization.

slide-9
SLIDE 9

1 The Problem 1.1 Preliminaries from Functional Programming

9

Input-output action types

In Haskell, there are special types for computations that introduce val- ues influenced by the environment. Namely, type IO A contains actions that give rise to a value of type A potentially influ- enced by the environment. – This value is called the return value of the action.

slide-10
SLIDE 10

1 The Problem 1.1 Preliminaries from Functional Programming

10

Operating with memory in functional programming

In functional languages, computing with memory locations with changing content is possible but tricky. – The address of the location, the reference, must differ from the address of any variable. – In order to avoid side-effects arising from changing the contents, the contents are treated as potentially influenced by the environ- ment.

slide-11
SLIDE 11

1 The Problem 1.2 Global Variables

11

Global Variables

slide-12
SLIDE 12

1 The Problem 1.2 Global Variables

12

Global variable

In imperative languages, global variables justify the words: – their scope is the whole module; – their R-value can change during the computation. Global variables are typically used for holding data structures that are needed during the whole computation.

slide-13
SLIDE 13

1 The Problem 1.2 Global Variables

13

The big problem

The values of variables with global scope cannot change. A freely changing entity can be assigned only to local variables at their initialization.

slide-14
SLIDE 14

1 The Problem 1.2 Global Variables

14

What is the problem?

But wait: having a globally visible reference to a memory location is sufficient. – Because then any part of code can make use of the location via dereferencing. This means that only the variable that holds the reference has to be assured to be constant. But the reference corresponds to the L-value of the variable of imper- ative languages which never changes during the reading of its scope! What is then the problem?

slide-15
SLIDE 15

1 The Problem 1.3 Operations with References

15

Operations with References

slide-16
SLIDE 16

1 The Problem 1.3 Operations with References

16

Input-output references

In the case of input-output references, the changing contents of memory locations are handled by the same mechanism as the influence

  • f environment.

Input-output reference types and operations with them are exported by the non-standard module Data.IORef.

slide-17
SLIDE 17

1 The Problem 1.3 Operations with References

17

Type

Type IORef A contains references to entities of type A.

slide-18
SLIDE 18

1 The Problem 1.3 Operations with References

18

Reading and writing

References are used by the following operators: reading (dereferencing): readIORef :: IORef a -> IO a, writing (updating): writeIORef :: IORef a -> a -> IO ().

slide-19
SLIDE 19

1 The Problem 1.3 Operations with References

19

Creation

Creating a new reference together with initialization: newIORef :: a -> IO (IORef a). Note that creating a reference gives an action rather than the reference itself. Hence, the reference is only accessible locally. ..

slide-20
SLIDE 20

1 The Problem 1.3 Operations with References

20

The root of the initial problem

A new reference (the address of a fresh memory location) is a bit of information from the environment. – If the value of expressions of the form newIORef a were ref- erences, the value of such an expression should not depend on the value of the argument a. ∗ In other words, referential transparency would demand that equal initial values implied equal memory locations. Of course, this is the last to be desired.

slide-21
SLIDE 21

1 The Problem 1.3 Operations with References

21

State thread references

There is another kind of references that rely on state thread actions rather than input-output actions. – State thread actions enable to make difference between compu- tations with result really independent from the environment and

  • thers.

From the formers, the result can be used without restrictions (un- like in the case of input-output actions). – They make use of higher-order polymorphism. Using these references ends up with the same problem as input-output references.

slide-22
SLIDE 22

2 Solutions

22

Solutions

slide-23
SLIDE 23

2 Solutions 2.1 No Globals

23

No Globals

slide-24
SLIDE 24

2 Solutions 2.1 No Globals

24

No globals

This means that all entities that must be used everywhere over the code must be passed around as parameters.

slide-25
SLIDE 25

2 Solutions 2.1 No Globals

25

Advantages

  • Perhaps improves flexibility: explicit parameters can overuse same

names etc.

slide-26
SLIDE 26

2 Solutions 2.1 No Globals

26

Drawbacks

  • More work for the programmer.
  • Clunky code if compared to imperative languages.
  • Implementations of algorithms differ from their classical versions

considerably.

slide-27
SLIDE 27

2 Solutions 2.1 No Globals

27

Corollary

This approach is a non-solution.

slide-28
SLIDE 28

2 Solutions 2.2 Unsafe operations

28

Unsafe operations

slide-29
SLIDE 29

2 Solutions 2.2 Unsafe operations

29

Operator unsafePerformIO

In GHC and Hugs, there is a non-standard module System.IO.Unsafe that provides unsafePerformIO :: IO a -> a. With this, one can simply take out the return value from an action. This way, one could create references for global data and make them globally visible.

slide-30
SLIDE 30

2 Solutions 2.2 Unsafe operations

30

Advantages

  • Easy to use.
  • The references created are truly global.
slide-31
SLIDE 31

2 Solutions 2.2 Unsafe operations

31

Drawbacks

  • This is not functional programming.

– Violates referential transparency. An operator like unsafePerformIO should not exist!

  • unsafePerformIO is unsafe, using it is undesirable for an aver-

age programmer. – Using it, one can easily create runtime type errors etc., that are supposed to never arise in functional programming.

slide-32
SLIDE 32

2 Solutions 2.2 Unsafe operations

32

Corollary

This approach is not a solution.

slide-33
SLIDE 33

2 Solutions 2.3 Reader Monads

33

Reader Monads

slide-34
SLIDE 34

2 Solutions 2.3 Reader Monads

34

Reader monads

If M is a monad and R is an arbitrary type then the type function mapping any type A to type R → M A is also a monad, so-called reader monad. The idea: – there is one copy of M A for each value from type R – and the usual programming in monad M goes on, in all copies simultaneously.

slide-35
SLIDE 35

2 Solutions 2.3 Reader Monads

35

Point-free style

In principle, this requires programming in point-free style. – All definitions are specified at the function level. Arguments are not explicitly given.

slide-36
SLIDE 36

2 Solutions 2.3 Reader Monads

36

Advantages

  • It does not go beyond the functional paradigm.
  • It enables to hide the parameter that is passed around.
slide-37
SLIDE 37

2 Solutions 2.3 Reader Monads

37

Drawbacks

  • Hard to understand by average programmers.
  • The references are not really global.
  • Instead of repeating the reference parameter, other clunky repeating

things are needed in order to keep type correctness.

slide-38
SLIDE 38

2 Solutions 2.3 Reader Monads

38

Corollary

This approach addresses a wrong problem. – The initial problem was not in code repetition but in accessibil- ity.

slide-39
SLIDE 39

2 Solutions 2.4 Implicit Parameters

39

Implicit Parameters

slide-40
SLIDE 40

2 Solutions 2.4 Implicit Parameters

40

Implicit parameters

Implicit parameters are implicit in the code function arguments that are nevetheless consumed. – Parametric polymorphism. – GHC enables also implicit data parameters.

slide-41
SLIDE 41

2 Solutions 2.4 Implicit Parameters

41

The principles

  • Implicit paramters are shown by the type context.
  • The names of implicit parameters are visible in the definition. (They

start with question mark.)

  • At the call of an operator having implicit parameters from a context

having the same implicit parameters, the values of these parameters are silently passed unchanged, if the code does not say otherwise.

  • Implicit data parameters can be bound also explicitly (e.g., in a code

fragment whose context does not contain this parameter). Implicit data parameters can be (and, in GHC, actually are) imple- mented via dynamic scoping.

slide-42
SLIDE 42

2 Solutions 2.4 Implicit Parameters

42

Advantages

  • It does not go beyond the functional paradigm.
  • The parameter is passed silently where necessary.
  • Easy to learn.
  • Little trouble in using.
slide-43
SLIDE 43

2 Solutions 2.4 Implicit Parameters

43

Drawbacks

  • Implicit parameters are not truly global.
  • Implicit parameters still appear in type contexts.
slide-44
SLIDE 44

2 Solutions 2.4 Implicit Parameters

44

Corollary

Implicit parameters are surprisingly useful but the solution to our prob- lem is supposititious. (Again, the wrong problem is addressed.)

slide-45
SLIDE 45

2 Solutions 2.5 My 2 cents

45

My 2 cents

slide-46
SLIDE 46

2 Solutions 2.5 My 2 cents

46

Variable type family

A central module Var could declare a data family Var :: * -> *. For each type A, it gives a type of “variables of type A”. – User modules can provide the real variable types as data in- stances. – This way: ∗ variables get names ∗ whereby, technically, these names are data constructors.

slide-47
SLIDE 47

2 Solutions 2.5 My 2 cents

47

Explicit variable environment

A variable environment is a function from variables of some type to references to data of that type: type Env a = Var a -> IORef a . – For efficiency, this function should be implemented as a lookup from an array. ∗ Note that the array would be read-only.

slide-48
SLIDE 48

2 Solutions 2.5 My 2 cents

48

Global environment

Another central module Global could then implement global envi- ronment: – a type class AbstrRef of type pairs, relating, by intention, variable types with corresponding refer- ence types; – a class method globalEnv :: Env a that is implemented as unsafe creation of a new environment. ∗ This use of unsafePerformIO is safe as globalEnv is evaluated only once.

slide-49
SLIDE 49

2 Solutions 2.5 My 2 cents

49

Advantages

  • Users do not have to go beyond functional programming.

– Unsafe operator occurs only in the central module. – Alternatively, the explicit global environment could be imple- mented directly into the language by its designers. ∗ Referential transparency is not violated as the references are depending on variables rather than their initial values.

  • References are truly global.
  • Easy to use.
  • Code becomes close to mainstream imperative programming.
slide-50
SLIDE 50

2 Solutions 2.5 My 2 cents

50

Drawbacks

  • All globals of the same type that are used simultaneously must be

introduced together, in one declaration. – Haskell does not have open data declarations. – But this drawback for programmers may be considered as an ad- vantage for readers.