
Purely Functional Data Structures and Monoids Donnacha Ois´ın Kidney May 9, 2020 1 Purely Functional Data Structures To answer that question, we’re going to look at a very simple algorithm in an imperative language, and we’re going to see how not to translate it into Haskell. The mistake we make may well be one which you have made in past! Why Do We Need Them? Why do pure functional languages need a dierent way to do data structures? Why can’t we just use traditional algorithms from imperative programming? 2 The mistake we make may well be one which you have made in past! Why Do We Need Them? Why do pure functional languages need a dierent way to do data structures? Why can’t we just use traditional algorithms from imperative programming? To answer that question, we’re going to look at a very simple algorithm in an imperative language, and we’re going to see how not to translate it into Haskell. 2 Why Do We Need Them? Why do pure functional languages need a dierent way to do data structures? Why can’t we just use traditional algorithms from imperative programming? To answer that question, we’re going to look at a very simple algorithm in an imperative language, and we’re going to see how not to translate it into Haskell. The mistake we make may well be one which you have made in past! 2 A Simple Imperative Algorithm 3 A Simple Imperative Algorithm (in Python) 3 A Simple Imperative Algorithm We’re going to write a func- tion to create an array filled with some ints. 3 A Simple Imperative Algorithm It works like this. >>> create_array_up_to(5) [0,1,2,3,4] 3 A Simple Imperative Algorithm def create_array_up_to(n): This is its implementa- array=[] tion. for i in range(n): array.append(i) return array 3 A Simple Imperative Algorithm def create_array_up_to(n): We first initialise an empty array=[] ( array. for i in range(n): array.append(i) return array 3 A Simple Imperative Algorithm def create_array_up_to(n): And then we loop through array=[] the numbers from 0 to for i in range(n): n-1. ( array.append(i) return array 3 A Simple Imperative Algorithm def create_array_up_to(n): We append each number on array=[] to the array. for i in range(n): array.append(i) ( return array 3 A Simple Imperative Algorithm def create_array_up_to(n): array=[] And we return the array. for i in range(n): array.append(i) return array ( 3 A Simple Imperative Algorithm def create_array_up_to(n): array=[] for i in range(n): array.append(i) return array >>> create_array_up_to(5) [0,1,2,3,4] 3 def create_array_up_to(n): We’re going to run into a problem array=[] for in with this line. i range(n): array.append(i) ( return array The append function mutates array: aer calling append, the value of the variable array changes. array has dierent values before and aer line 3. We can’t do that in an immutable language! A variable’s value cannot change from one line to the next in Haskell. Trying to Translate it to Haskell 4 The append function mutates array: aer calling append, the value of the variable array changes. array has dierent values before and aer line 3. We can’t do that in an immutable language! A variable’s value cannot change from one line to the next in Haskell. Trying to Translate it to Haskell def create_array_up_to(n): We’re going to run into a problem array=[] for in with this line. i range(n): array.append(i) ( return array 4 array has dierent values before and aer line 3. We can’t do that in an immutable language! A variable’s value cannot change from one line to the next in Haskell. Trying to Translate it to Haskell def create_array_up_to(n): We’re going to run into a problem array=[] for in with this line. i range(n): array.append(i) ( return array The append function mutates array: aer calling append, the value of the variable array changes. 4 We can’t do that in an immutable language! A variable’s value cannot change from one line to the next in Haskell. Trying to Translate it to Haskell def create_array_up_to(n): We’re going to run into a problem array=[] for in with this line. i range(n): array.append(i) ( return array The append function mutates array: 1 array=[1,2,3] aer calling append, the value of the 2 print(array) variable array changes. 3 array.append(4) array has dierent values before and 4 print(array) aer line 3. 4 Trying to Translate it to Haskell def create_array_up_to(n): We’re going to run into a problem array=[] for in with this line. i range(n): array.append(i) ( return array The append function mutates array: 1 array=[1,2,3] aer calling append, the value of the 2 print(array) variable array changes. 3 array.append(4) array has dierent values before and 4 print(array) aer line 3. We can’t do that in an immutable language! A variable’s value cannot change from one line to the next in Haskell. 4 append :: Array a ! a ! Array a main = do myArray = [1; 2; 3] print myArray myArray2 = myArray `append` 4 print myArray2 Append in Haskell Instead of mutating variables, in Haskell when we want to change a data structure we usually write a function which returns a new variable equal to the old data structure with the change applied. 5 main = do myArray = [1; 2; 3] print myArray myArray2 = myArray `append` 4 print myArray2 Append in Haskell Instead of mutating variables, in Haskell when we want to change a data structure we usually write a function which returns a new variable equal to the old data structure with the change applied. append :: Array a ! a ! Array a 5 Append in Haskell Instead of mutating variables, in Haskell when we want to change a data structure we usually write a function which returns a new variable equal to the old data structure with the change applied. append :: Array a ! a ! Array a main = do myArray = [1; 2; 3] print myArray myArray2 = myArray `append` 4 print myArray2 5 foldl (λarray i ! append array i) emptyArray [0 :: n − 1] createArrayUpTo :: Int ! Array Int createArrayUpTo n = O(n) O(n2) Translating it to Haskell Let’s look at the imperative algorithm, and try to translate it bit-by-bit. 6 foldl (λarray i ! append array i) emptyArray [0 :: n − 1] createArrayUpTo :: Int ! Array Int createArrayUpTo n = O(n) O(n2) Translating it to Haskell def create_array_up_to(n): array=[] for i in range(n): array.append(i) return array First we’ll need to write the type signature and skeleton of the Haskell function. What should the type be? 6 foldl (λarray i ! append array i) emptyArray [0 :: n − 1] O(n) O(n2) Translating it to Haskell createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] for i in range(n): array.append(i) return array 6 foldl (λarray i ! append array i) emptyArray [0 :: n − 1] O(n) O(n2) Translating it to Haskell createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] for i in range(n): array.append(i) return array We tend not to use loops in functional languages, but this loop in particular follows a very common paern which has a name and function in Haskell. What is it? 6 (λarray i ! append array i) emptyArray O(n) O(n2) Translating it to Haskell createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] foldl for i in range(n): array.append(i) return array [0 :: n − 1] foldl is the function we need. How would the output have diered if we used foldr instead? 6 (λarray i ! append array i) emptyArray O(n) O(n2) Translating it to Haskell createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] foldl for i in range(n): array.append(i) return array [0 :: n − 1] 6 (λarray i ! append array i) O(n) O(n2) Translating it to Haskell createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] foldl for i in range(n): array.append(i) emptyArray return array [0 :: n − 1] 6 (λarray i ! append array i) O(n) O(n2) Translating it to Haskell createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] foldl for i in range(n): array.append(i) emptyArray return array [0 :: n − 1] 6 O(n) O(n2) Translating it to Haskell createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] foldl for i in range(n): (λarray i ! append array i) array.append(i) emptyArray return array [0 :: n − 1] Is there a shorter way to write this, that doesn’t include a lambda? 6 Translating it to Haskell createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] foldl for i in range(n): (λarray i ! append array i) array.append(i) emptyArray return array [0 :: n − 1] O(n) O(n2) 6 Why the performance dierence? 6 It comes down to the dierent complexities of append. Python Haskell O(1) O(n) createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] foldl for i in range(n): (λarray i ! append array i) array.append(i) emptyArray return array [0 :: n − 1] Both implementations call append n times, which causes the dierence in asymptotics. Why the performance dierence? 7 Python Haskell O(1) O(n) createArrayUpTo :: Int ! Array Int def create_array_up_to(n): createArrayUpTo n = array=[] foldl for i in range(n): (λarray i ! append array i) array.append(i) emptyArray return array [0 :: n − 1] Both implementations call append n times, which causes the dierence in asymptotics.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages148 Page
-
File Size-