Principles of Programming Languages www.cs.bgu.ac.il/~ppl172 Collaboration and Management Dana Fisman Lesson 5 - Data Types and Operations on Data 1
Types - what we already know Types define sets of values There are atomic and compound types The programmer can define data types and annotate its code in order to reflect the intended meaning of variables and functions A process called type checking can analyze the program to verify there are no type discrepancies 2
Types - third important role Type definitions help the programmer structure the code that operates on complex data values in a way that reflects the structure of the data type. Knowing the structure of a data type helps the programmer write correct functions that operate over values of this type. And vice versa, the decision on how to design the data type is driven by the operations that need to be preformed on them, and the desire to make them uniform. 3
Types - third important role We will illustrate this point through four examples: Homogeneous array types and the sequence interface (map, filter, reduce, ) Disjoint types and Disjoint Unions to enable uniform functional interfaces Modeling trees Mutable data types in FP 4
Homogeneous Array Types and the Sequence Interface Array values can be o homogeneous (all items are of the same types) or o heterogeneous (not all items are of the same type) The natural way to process homogenous arrays is using the sequence interface (the set of higher order functions which can be applied to them) o map o filter o reduce o and others (every, some, find, etc.) 5
Homogeneous Array Types and the Sequence Interface 6
Homogeneous Array Types and the Sequence Interface Can we always apply map(f,array) on array? When can we? When o the array is of type <SomeType>[] and o f is of type <SomeType> => <PossiblyAnotherType> 7
Homogeneous Array Types and the Sequence Interface When can we apply filter (p?,array) on array? When o the array is of type <SomeType>[] and o p? is of type <SomeType> => boolean Heterogenous arrays do not lend themselves to map etc. When can we apply reduce (f, init, array) on array? When o the array is of type <SomeType>[] and o f is of type <PossiblyOtherType>,<SomeType> => <PossiblyOtherType> o init is of type <PossiblyOtherType> 8
Creating new data types We have seen various ways to create new data types: Array types Map types Generic types We have discussed the set relations among created data types. Can we create new data types simply using set relations? Cartesian product? Union? Intersection? 9
Typescript Union and Intersection We can we create new data types simply using typescript s union and intersection type NoS = number string; type SoB = string boolean; type S = NoS & SoB; 10
Structural vs. Nominal Subtyping Compiles ok! 11 Under nominal typing (as in Java) these two types are disjoint. But sometimes we do want to distinguish these! Under structural typing (as in Javascript) these two types are equivalent.
Disjoint Types Compilation error: Type 'Person' is not assignable to type Variable'. Types of property 'tag' are incompatible. The addition of field tag caused the two types to be disjoint! 12 Does not compile as desired
Disjoint Union Mathematical definition: By combining type union operator and a mutual field (tag) with distinct values we can obtain disjoint types. 13
14 Disjoint Union
Disjoint Union 15 disjoint union together with the corresponding switch construct achieves an effect similar to sub-classes with virtual classes in OOP It allows the function to dispatch to different computations based on the type of the actual value received as a parameter.
Modeling Trees with Types The function processing the data type is structured in the same way Different fields have different assurance on their existence Using type analysis we can verify we are safe and exhaustive 16 bt = { root :2, "left":{"root":3,"left":{"root":4}}, "right":{"root":5,"right":{"root":6}}} traversedfs(bt); ==>2 3 4 5 6
Modeling Trees with Types Being exhaustive resulted in lots of repeated code 17 squaretree(bt2); ==> { root: 4, left: { root: 9, left: { root: 16 } }, right: { root: 25, right: { root: 36 } } }
Modeling Trees with Types By relaxing type checking and accepting undefined as a return value we can make the code shorter and more readable: 18
Modeling Trees with Types Output: squaretree2(bt) { root: 4, left: { root: 9, left: { root: 16, left: undefined, right: undefined }, right: undefined }, right: { root: 25, left: undefined, right: { root: 36, left: undefined, right: undefined } } } 19
Modeling Trees with Types Let us reflect on this version: The expected values of type BinTree were extended to include undefined We explicitly test for undefined as the first base case in the recursive function. The recursive calls are now simplified as we can avoid the recursive calls with a value undefined The return value has values marked explicitly as undefined - these are semantically equivalent to absent values - but in the syntax of the object, they still appear. 20
Modeling Trees with Types How can we eliminate the undefined in the output? Using JSON.stringify() The two version correspond to different styles undefined may complicate analysis but is hard to avoid 21
Mutable Data Types in FP Suppose we want to implement in FP a stack, which is mutable by nature? push(x) pop() Mutate. And return a value. empty() Query. Does not mutate. 22
Mutable Data Types in FP Let us separate to pure queries and pure mutators push(x) pop() peek() empty() Mutators Queries This definition is still inherently procedural The result maybe that even queries are not deterministic Can return different values on same calls 23
Mutable Data Types in FP Non-Functional Implementation Generic Constructor Queries. No mutations. Mutators Relies on arrays being mutable 24
Mutable Data Types in FP Can a non-deterministic behavior of queries really occur? Peek(s) on the same value s, returns different results 25
Mutable Data Types in FP Functional Implementation (not efficient) Generic Cloning utility (shallow) Constructor Queries. No mutations. Return new copy 26
Mutable Data Types in FP Does it provides a deterministic behavior? Using this implementat ion requires a change from the client side: the interface has a changed! 27 Yes! This implementation is safe But inefficient!
Mutable Data Types in FP What is the remedy? Use efficient immutable libraries such as immutable-js by Facebook 28
Mutable Data Types in FP The immutablle-js library works well with JSON So we can e.g. construct a Stack for a string 29
Summary Type definitions help the programmer structure the code that operates on complex values in a way that reflects the structure of the data type. Programmers design and name types for sets of values that will be processed by the same set of functions. Homogeneous array types encourage the programmer to consume them using the sequence interface (map, filter, reduce) 30
Summary Recursive types are processed by recursive functions othe recursive functions inspect the values according to the type definition oand can determine the base case and the inductive case. Mutable data types can be modeled in a Functional Programming style by making sure commands are written as constructors which return a new version of the values (instead of mutating an existing value). Disjoint types and Disjoint Unions enable the definition of uniform functional interfaces over types of values that are not structurally similar. 31