(download for perfect quality) - See how recursive functions and structural induction relate to recursive datatypes.
Follow along as the fold abstraction is introduced and explained.
Watch as folding is used to simplify the definition of recursive functions over recursive datatypes
Part 1 - through the work of Richard Bird and Graham Hutton.
This version corrects the following issues:
slide 7, 11 fib(0) is 0,rather than 1
slide 23: was supposed to be followed by 2-3 slides recapitulating definitions of factorial and fibonacci with and without foldr, plus translation to scala
slide 36: concat not invoked in concat example
slides 48 and 49: unwanted 'm' in definition of sum
throughout: a couple of typographical errors
throughout: several aesthetic imperfections (wrong font, wrong font colour)
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...Philip Schwarz
First see the problem solved using the List monad and a Scala for comprehension.
Then see the Scala program translated into Haskell, both using a do expressions and using a List comprehension.
Understand how the Scala for comprehension is desugared, and what role the withFilter function plays.
Also understand how the Haskell do expressions and List comprehension are desugared, and what role the guard function plays.
Scala code for Part 1: https://github.com/philipschwarz/n-queens-combinatorial-problem-scala-part-1
Errata: on slide 30, the resulting lists should be Haskell ones rather than Scala ones.
The Functional Programming Toolkit (NDC Oslo 2019)Scott Wlaschin
(slides and video at https://fsharpforfunandprofit.com/fptoolkit)
The techniques and patterns used in functional programming are very different from object-oriented programming, and when you are just starting out it can be hard to know how they all fit together.
In this big picture talk for FP beginners, I'll present some of the common tools that can be found in a functional programmer's toolbelt; tools such as "map", "apply", "bind", and "sequence". What are they? Why are they important? How are they used in practice? And how do they relate to scary sounding concepts like functors, monads, and applicatives?
The lazy programmer's guide to writing thousands of testsScott Wlaschin
We are all familiar with example-based testing, as typified by TDD and BDD, where each test is hand-crafted.
But there's another approach to writing tests. In the "property-based testing" approach, a single test is run hundreds of times with randomly generated inputs. Property-based testing is a great way to find edge cases, and also helps you to understand and document the behavior of your code under all conditions.
This talk will introduce property-based testing, show you how it works, and demonstrate why you should consider adding this powerful technique to your toolbelt.
Sierpinski Triangle - Polyglot FP for Fun and Profit - Haskell and ScalaPhilip Schwarz
Take the very first baby steps on the path to doing graphics in Haskell and Scala.
Learn about a simple yet educational recursive algorithm producing images that are pleasing to the eye.
Learn how functional programs deal with the side effects required to draw images.
See how libraries like Gloss and Doodle make drawing Sierpinski’s triangle a doddle.
Code for this slide deck:
https://github.com/philipschwarz/sierpinski-triangle-haskell-gloss
https://github.com/philipschwarz/sierpinski-triangle-scala-cats-io
https://github.com/philipschwarz/sierpinski-triangle-scala-awt-and-doodle
Errata:
1. the title 'Sierpinski Triangle' on the front slide could be improved by replacing it with 'Sierpinski's Triangle'.
2. a couple of typos on two slides
3. the triangles drawn using Doodle are not equilateral, as intended but isosceles.
(UPDATE 2021-06-15 I opened PR https://github.com/creativescala/doodle/pull/99 and as a result, an equilateral triangle has now been added to Doodle: https://github.com/creativescala/doodle/commit/30d20efebcc2016942e9cdbae85fefca5b95fa3c).
Here is a corrected version of the deck: https://www.slideshare.net/pjschwarz/sierpinski-triangle-polyglot-fp-for-fun-and-profit-haskell-and-scala-with-minor-corrections
(Video and code at http://fsharpforfunandprofit.com/composition)
Composition is a fundamental principle of functional programming, but how is it different from an object-oriented approach, and how do you use it in practice?
In this talk for beginners, we'll start by going over the basic concepts of functional programming, and then look at some different ways that composition can be used to build large things from small things.
After that, we'll see how composition is used in practice, beginning with a simple FizzBuzz example, and ending with a complete (object-free!) web application.
(video and more at http://fsharpforfunandprofit.com/fppatterns)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID. The functional programming community has design patterns and principles as well. This talk will provide an overview of some of these patterns (such as currying, monads), and present some demonstrations of FP design in practice. We'll also look at some of the ways you can use these patterns as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...Philip Schwarz
First see the problem solved using the List monad and a Scala for comprehension.
Then see the Scala program translated into Haskell, both using a do expressions and using a List comprehension.
Understand how the Scala for comprehension is desugared, and what role the withFilter function plays.
Also understand how the Haskell do expressions and List comprehension are desugared, and what role the guard function plays.
Scala code for Part 1: https://github.com/philipschwarz/n-queens-combinatorial-problem-scala-part-1
Errata: on slide 30, the resulting lists should be Haskell ones rather than Scala ones.
The Functional Programming Toolkit (NDC Oslo 2019)Scott Wlaschin
(slides and video at https://fsharpforfunandprofit.com/fptoolkit)
The techniques and patterns used in functional programming are very different from object-oriented programming, and when you are just starting out it can be hard to know how they all fit together.
In this big picture talk for FP beginners, I'll present some of the common tools that can be found in a functional programmer's toolbelt; tools such as "map", "apply", "bind", and "sequence". What are they? Why are they important? How are they used in practice? And how do they relate to scary sounding concepts like functors, monads, and applicatives?
The lazy programmer's guide to writing thousands of testsScott Wlaschin
We are all familiar with example-based testing, as typified by TDD and BDD, where each test is hand-crafted.
But there's another approach to writing tests. In the "property-based testing" approach, a single test is run hundreds of times with randomly generated inputs. Property-based testing is a great way to find edge cases, and also helps you to understand and document the behavior of your code under all conditions.
This talk will introduce property-based testing, show you how it works, and demonstrate why you should consider adding this powerful technique to your toolbelt.
Sierpinski Triangle - Polyglot FP for Fun and Profit - Haskell and ScalaPhilip Schwarz
Take the very first baby steps on the path to doing graphics in Haskell and Scala.
Learn about a simple yet educational recursive algorithm producing images that are pleasing to the eye.
Learn how functional programs deal with the side effects required to draw images.
See how libraries like Gloss and Doodle make drawing Sierpinski’s triangle a doddle.
Code for this slide deck:
https://github.com/philipschwarz/sierpinski-triangle-haskell-gloss
https://github.com/philipschwarz/sierpinski-triangle-scala-cats-io
https://github.com/philipschwarz/sierpinski-triangle-scala-awt-and-doodle
Errata:
1. the title 'Sierpinski Triangle' on the front slide could be improved by replacing it with 'Sierpinski's Triangle'.
2. a couple of typos on two slides
3. the triangles drawn using Doodle are not equilateral, as intended but isosceles.
(UPDATE 2021-06-15 I opened PR https://github.com/creativescala/doodle/pull/99 and as a result, an equilateral triangle has now been added to Doodle: https://github.com/creativescala/doodle/commit/30d20efebcc2016942e9cdbae85fefca5b95fa3c).
Here is a corrected version of the deck: https://www.slideshare.net/pjschwarz/sierpinski-triangle-polyglot-fp-for-fun-and-profit-haskell-and-scala-with-minor-corrections
(Video and code at http://fsharpforfunandprofit.com/composition)
Composition is a fundamental principle of functional programming, but how is it different from an object-oriented approach, and how do you use it in practice?
In this talk for beginners, we'll start by going over the basic concepts of functional programming, and then look at some different ways that composition can be used to build large things from small things.
After that, we'll see how composition is used in practice, beginning with a simple FizzBuzz example, and ending with a complete (object-free!) web application.
(video and more at http://fsharpforfunandprofit.com/fppatterns)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID. The functional programming community has design patterns and principles as well. This talk will provide an overview of some of these patterns (such as currying, monads), and present some demonstrations of FP design in practice. We'll also look at some of the ways you can use these patterns as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
Les01 (retrieving data using the sql select statement)Achmad Solichin
This document provides an overview of using SQL SELECT statements and the iSQLPlus environment to retrieve and work with data. It covers the basic capabilities and syntax of SELECT statements including selecting all or specific columns, arithmetic expressions, aliases, and null values. It also describes interacting with the iSQLPlus environment, such as describing table structures, running SQL statements from scripts, and setting preferences. The key aspects of SQL statements and iSQLPlus commands are differentiated.
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesLauren Yew
Kotlin Coroutines is a powerful threading library for Kotlin, released by JetBrains in 2018. At The New York Times, we recently migrated our core libraries and parts of our News app from RxJava to Kotlin Coroutines. In this talk we’ll share lessons learned and best practices to understand, migrate to, and use Kotlin Coroutines & Flows.
In this presentation, you will learn:
What Coroutines are and how they function
How to use Kotlin Coroutines & Flows (with real world examples and demos)
Where and why you should use Coroutines & Flows in your app
How to avoid the pitfalls of Coroutines
Kotlin Coroutines vs. RxJava
Lessons learned from migrating to Kotlin Coroutines from RxJava in large legacy projects & libraries
By the end of this talk, you will be able to apply Kotlin Coroutines to your own app, run the provided sample code yourself, and convince your team to give Kotlin Coroutines a try!
Functional Programming Patterns (NDC London 2014)Scott Wlaschin
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
Asynchronous API in Java8, how to use CompletableFutureJosé Paumard
Slides of my talk as Devoxx 2015. How to set up asynchronous data processing pipelines using the CompletionStage / CompletableFuture API, including how to control threads and how to handle exceptions.
There are a lot of interesting facts that are still unknown and make the Javascript the world's most confusing language. I've tried to explain a few of them if they can help you in better understanding of the JS code.
The document discusses the Uniform Access Principle, which states that all services offered by a module should be available through a uniform notation that does not reveal whether they are implemented through storage or computation. It provides examples of how different languages do or do not follow this principle, and explains that following this principle helps avoid breaking client code when implementation details change. It also discusses how Scala supports the Uniform Access Principle for parameterless methods.
Functional Domain Modeling - The ZIO 2 WayDebasish Ghosh
Principled way to design and implement functional domain models using some of the patterns of domain driven design. DDD, as the name suggests, is focused towards the domain model and the patterns of architecture that it encourages are also based on how we think of interactions amongst the basic abstractions of the domain. Of course the primary goal of the talk is to discuss how Scala and Zio 2 can be a potent combination in realizing the implementation of such models. This is not a talk on FP, the focus will be on how to structure and modularise an application based on some of the patterns of DDD.
RxJs - demystified provides an overview of reactive programming and RxJs. The key points covered are:
- Reactive programming focuses on propagating changes without explicitly specifying how propagation happens.
- Observables are at the heart of RxJs and emit values in a push-based manner. Operators allow transforming, filtering, and combining observables.
- Common operators include map, filter, reduce, buffer, and switchMap. Over 120 operators exist for tasks like error handling, multicasting, and conditional logic.
- Marble diagrams visually demonstrate how operators transform observable streams.
- Creating observables from events, promises, arrays and iterables allows wrapping different data sources in a uniform API
This slide contains short introduction to different elements of functional programming along with some specific techniques with which we use functional programming in Swift.
Design functional solutions in Java, a practical exampleMarian Wamsiedel
The document discusses different approaches to controlling vehicles remotely using vocal commands in Java, including:
1. An object-oriented approach modeling commands as classes and executing them sequentially.
2. A pure functional approach using immutable functions to model commands and folding them to execute sequentially without side effects.
3. A functional approach with effects, using Try monads to isolate exceptions and allow sequential execution while handling errors.
The document discusses object-oriented programming (OOP) and functional programming (FP). It argues that OOP and FP are not opposites and can be effectively combined. It addresses common myths about FP, such as that it is new, hard, or only suitable for mathematical problems. The document also compares how OOP and FP approach concepts like decomposition, composition, error handling, and mutability/referential transparency. While both approaches have merits, the document concludes there is no single best approach and the choice depends on the problem and programmer preferences.
Property Based Testing is an process to build robust systems.
It facilitates a deeper understanding of the system under test. It can be used on any testing level: unit, integration or functional.
The presentation introduces how Property Based Testing works, how to use it with PHPUnit, and in what way it differentiates from example based tests.
It talks about strategies to find good properties to check for.
This presentation was built for the Meet-Magento conference 2020 in Mumbai.
The document provides information on object-oriented programming concepts in Java including classes, objects, encapsulation, inheritance, polymorphism, and abstraction. It defines classes like Shape, Rectangle, Circle and Triangle to demonstrate these concepts. It also discusses Java data types, constructors, access modifiers, interfaces and abstract classes.
Domain Driven Design with the F# type System -- NDC London 2013Scott Wlaschin
(Video of these slides here http://fsharpforfunandprofit.com/ddd)
Statically typed functional programming languages like F# encourage a very different way of thinking about types. The type system is your friend, not an annoyance, and can be used in many ways that might not be familiar to OO programmers.
Types can be used to represent the domain in a fine-grained, self documenting way. And in many cases, types can even be used to encode business rules so that you literally cannot create incorrect code. You can then use the static type checking almost as an instant unit test — making sure that your code is correct at compile time.
In this talk, we'll look at some of the ways you can use types as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
Code, links to video, etc., at http://fsharpforfunandprofit.com/ddd
For more on DDD and F# see:
http://fsharpforfunandprofit.com/ddd/
http://tomasp.net/blog/type-first-development.aspx/
http://gorodinski.com/blog/2013/02/17/domain-driven-design-with-fsharp-and-eventstore/
The document discusses clean coding practices for Java developers. It covers topics such as choosing meaningful names for variables, methods, and classes; writing code that is easy for others to understand; breaking methods down into single logical steps; and using fluent APIs to make code more readable. The presentation provides examples of clean code and ways to refactor code to follow best practices.
Java 8 is coming soon. In this presentation I have outlined the major Java 8 features. You get information about interface improvements, functional interfaces, method references, lambdas, java.util.function, java.util.stream
You may be hearing a lot of buzz around functional programming. For example, Java 8 recently introduced new features (lambda expressions and method references) and APIs (Streams, Optional and CompletableFutures) inspired from functional ideas such as first-class functions, composition and immutability.
However, what does this mean for my existing codebase?
In this talk we show how you can refactor your traditional object-oriented Java to using FP features and APIs from Java 8 in a beneficial manner.
We will discuss:
- How to adapt to requirement changes using first-class functions
- How you can enhance code reusability using currying
- How you can make your code more robust by favouring immutability over mutability
- How you can design better APIs and reduce unintended null pointer exceptions using an optional data type"
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
- The document describes how product types (built using AND) and sum types (built using OR) are used to define types for representing fruit salads and fruit snacks in F#, Haskell, Scala, and Java.
- Product types combine elements and are used to define the FruitSalad type, while sum types allow alternatives and are used to define the FruitSnack type consisting of different fruits.
- Pattern matching is demonstrated to write functions that analyze and return different strings based on the values of FruitSalad and FruitSnack types.
RxJS is a library for reactive programming that allows composing asynchronous and event-based programs using observable sequences. It provides the Observable type for pushing multiple values to observers over time asynchronously. Operators allow transforming and combining observables. Key types include Observable, Observer, Subject, BehaviorSubject, and ReplaySubject. Subjects can multicast values to multiple observers. Overall, RxJS is useful for handling asynchronous events as collections in a declarative way.
This document provides information about lambda calculus and combinators. It includes definitions and examples of:
- Beta reduction and how it works with functions
- Church numerals for representing numbers
- Defining basic operations like addition and multiplication
- Boolean logic using true, false, and, or, not, cond
- Pairs and accessing elements
- Moses Schönfinkel who invented combinators
- The three basic combinators: I, K, S and what they represent
The document discusses the benefits of declarative programming using Scala. It provides examples of implementing algorithms and data structures declaratively in Scala. It also discusses the history and future of Scala, as well as how Scala encourages thinking about programs as transformations rather than changes to memory.
Les01 (retrieving data using the sql select statement)Achmad Solichin
This document provides an overview of using SQL SELECT statements and the iSQLPlus environment to retrieve and work with data. It covers the basic capabilities and syntax of SELECT statements including selecting all or specific columns, arithmetic expressions, aliases, and null values. It also describes interacting with the iSQLPlus environment, such as describing table structures, running SQL statements from scripts, and setting preferences. The key aspects of SQL statements and iSQLPlus commands are differentiated.
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesLauren Yew
Kotlin Coroutines is a powerful threading library for Kotlin, released by JetBrains in 2018. At The New York Times, we recently migrated our core libraries and parts of our News app from RxJava to Kotlin Coroutines. In this talk we’ll share lessons learned and best practices to understand, migrate to, and use Kotlin Coroutines & Flows.
In this presentation, you will learn:
What Coroutines are and how they function
How to use Kotlin Coroutines & Flows (with real world examples and demos)
Where and why you should use Coroutines & Flows in your app
How to avoid the pitfalls of Coroutines
Kotlin Coroutines vs. RxJava
Lessons learned from migrating to Kotlin Coroutines from RxJava in large legacy projects & libraries
By the end of this talk, you will be able to apply Kotlin Coroutines to your own app, run the provided sample code yourself, and convince your team to give Kotlin Coroutines a try!
Functional Programming Patterns (NDC London 2014)Scott Wlaschin
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
Asynchronous API in Java8, how to use CompletableFutureJosé Paumard
Slides of my talk as Devoxx 2015. How to set up asynchronous data processing pipelines using the CompletionStage / CompletableFuture API, including how to control threads and how to handle exceptions.
There are a lot of interesting facts that are still unknown and make the Javascript the world's most confusing language. I've tried to explain a few of them if they can help you in better understanding of the JS code.
The document discusses the Uniform Access Principle, which states that all services offered by a module should be available through a uniform notation that does not reveal whether they are implemented through storage or computation. It provides examples of how different languages do or do not follow this principle, and explains that following this principle helps avoid breaking client code when implementation details change. It also discusses how Scala supports the Uniform Access Principle for parameterless methods.
Functional Domain Modeling - The ZIO 2 WayDebasish Ghosh
Principled way to design and implement functional domain models using some of the patterns of domain driven design. DDD, as the name suggests, is focused towards the domain model and the patterns of architecture that it encourages are also based on how we think of interactions amongst the basic abstractions of the domain. Of course the primary goal of the talk is to discuss how Scala and Zio 2 can be a potent combination in realizing the implementation of such models. This is not a talk on FP, the focus will be on how to structure and modularise an application based on some of the patterns of DDD.
RxJs - demystified provides an overview of reactive programming and RxJs. The key points covered are:
- Reactive programming focuses on propagating changes without explicitly specifying how propagation happens.
- Observables are at the heart of RxJs and emit values in a push-based manner. Operators allow transforming, filtering, and combining observables.
- Common operators include map, filter, reduce, buffer, and switchMap. Over 120 operators exist for tasks like error handling, multicasting, and conditional logic.
- Marble diagrams visually demonstrate how operators transform observable streams.
- Creating observables from events, promises, arrays and iterables allows wrapping different data sources in a uniform API
This slide contains short introduction to different elements of functional programming along with some specific techniques with which we use functional programming in Swift.
Design functional solutions in Java, a practical exampleMarian Wamsiedel
The document discusses different approaches to controlling vehicles remotely using vocal commands in Java, including:
1. An object-oriented approach modeling commands as classes and executing them sequentially.
2. A pure functional approach using immutable functions to model commands and folding them to execute sequentially without side effects.
3. A functional approach with effects, using Try monads to isolate exceptions and allow sequential execution while handling errors.
The document discusses object-oriented programming (OOP) and functional programming (FP). It argues that OOP and FP are not opposites and can be effectively combined. It addresses common myths about FP, such as that it is new, hard, or only suitable for mathematical problems. The document also compares how OOP and FP approach concepts like decomposition, composition, error handling, and mutability/referential transparency. While both approaches have merits, the document concludes there is no single best approach and the choice depends on the problem and programmer preferences.
Property Based Testing is an process to build robust systems.
It facilitates a deeper understanding of the system under test. It can be used on any testing level: unit, integration or functional.
The presentation introduces how Property Based Testing works, how to use it with PHPUnit, and in what way it differentiates from example based tests.
It talks about strategies to find good properties to check for.
This presentation was built for the Meet-Magento conference 2020 in Mumbai.
The document provides information on object-oriented programming concepts in Java including classes, objects, encapsulation, inheritance, polymorphism, and abstraction. It defines classes like Shape, Rectangle, Circle and Triangle to demonstrate these concepts. It also discusses Java data types, constructors, access modifiers, interfaces and abstract classes.
Domain Driven Design with the F# type System -- NDC London 2013Scott Wlaschin
(Video of these slides here http://fsharpforfunandprofit.com/ddd)
Statically typed functional programming languages like F# encourage a very different way of thinking about types. The type system is your friend, not an annoyance, and can be used in many ways that might not be familiar to OO programmers.
Types can be used to represent the domain in a fine-grained, self documenting way. And in many cases, types can even be used to encode business rules so that you literally cannot create incorrect code. You can then use the static type checking almost as an instant unit test — making sure that your code is correct at compile time.
In this talk, we'll look at some of the ways you can use types as part of a domain driven design process, with some simple real world examples in F#. No jargon, no maths, and no prior F# experience necessary.
Code, links to video, etc., at http://fsharpforfunandprofit.com/ddd
For more on DDD and F# see:
http://fsharpforfunandprofit.com/ddd/
http://tomasp.net/blog/type-first-development.aspx/
http://gorodinski.com/blog/2013/02/17/domain-driven-design-with-fsharp-and-eventstore/
The document discusses clean coding practices for Java developers. It covers topics such as choosing meaningful names for variables, methods, and classes; writing code that is easy for others to understand; breaking methods down into single logical steps; and using fluent APIs to make code more readable. The presentation provides examples of clean code and ways to refactor code to follow best practices.
Java 8 is coming soon. In this presentation I have outlined the major Java 8 features. You get information about interface improvements, functional interfaces, method references, lambdas, java.util.function, java.util.stream
You may be hearing a lot of buzz around functional programming. For example, Java 8 recently introduced new features (lambda expressions and method references) and APIs (Streams, Optional and CompletableFutures) inspired from functional ideas such as first-class functions, composition and immutability.
However, what does this mean for my existing codebase?
In this talk we show how you can refactor your traditional object-oriented Java to using FP features and APIs from Java 8 in a beneficial manner.
We will discuss:
- How to adapt to requirement changes using first-class functions
- How you can enhance code reusability using currying
- How you can make your code more robust by favouring immutability over mutability
- How you can design better APIs and reduce unintended null pointer exceptions using an optional data type"
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
- The document describes how product types (built using AND) and sum types (built using OR) are used to define types for representing fruit salads and fruit snacks in F#, Haskell, Scala, and Java.
- Product types combine elements and are used to define the FruitSalad type, while sum types allow alternatives and are used to define the FruitSnack type consisting of different fruits.
- Pattern matching is demonstrated to write functions that analyze and return different strings based on the values of FruitSalad and FruitSnack types.
RxJS is a library for reactive programming that allows composing asynchronous and event-based programs using observable sequences. It provides the Observable type for pushing multiple values to observers over time asynchronously. Operators allow transforming and combining observables. Key types include Observable, Observer, Subject, BehaviorSubject, and ReplaySubject. Subjects can multicast values to multiple observers. Overall, RxJS is useful for handling asynchronous events as collections in a declarative way.
This document provides information about lambda calculus and combinators. It includes definitions and examples of:
- Beta reduction and how it works with functions
- Church numerals for representing numbers
- Defining basic operations like addition and multiplication
- Boolean logic using true, false, and, or, not, cond
- Pairs and accessing elements
- Moses Schönfinkel who invented combinators
- The three basic combinators: I, K, S and what they represent
The document discusses the benefits of declarative programming using Scala. It provides examples of implementing algorithms and data structures declaratively in Scala. It also discusses the history and future of Scala, as well as how Scala encourages thinking about programs as transformations rather than changes to memory.
The Functional Programming Triad of Folding, Scanning and Iteration - a first...Philip Schwarz
The document discusses implementing various functional programming concepts like folding, scanning, and iteration to solve problems involving converting between digit sequences and integers. It provides examples in Scala and Haskell of using fold left to implement a digits-to-integer function and the iterate function to implement an integer-to-digits function. It also discusses using techniques like pipes in Scala and the $ operator in Haskell to improve readability by ordering function applications in the sequence they will be executed.
The Functional Programming Triad of Folding, Scanning and Iteration - a first...Philip Schwarz
This slide deck can work both as an aide mémoire (memory jogger), or as a first (not completely trivial) example of using left folds, left scans and iteration, to implement mathematical induction.
Errata: on almost half of the slides there is some minor typo or imperfection or in some cases a minor error and in one case, an omission. See a later version for corrections and some improvements.
(for best quality images, either download or view here: https://philipschwarz.dev/fpilluminated/?page_id=455)
Scala code for latest version: https://github.com/philipschwarz/fp-fold-scan-iterate-triad-a-first-example-scala
The document discusses Haskell concepts including:
1) Pattern matching allows functions to handle different cases depending on the structure of input data, like matching empty and non-empty lists.
2) Guards allow selecting different code blocks based on Boolean conditions.
3) Combining pattern matching and guards allows for very expressive functions that concisely handle multiple cases.
4) Metasyntactic variables like (x:xs) follow conventions to bind pattern names and make code more readable.
5) Functions can match multiple patterns at once to handle different input structures.
Presented online for javaBin (2020-04-14)
Video at https://www.youtube.com/watch?v=orcSUE0Jjdc
Lambdas. All the cool kid languages have them. But does ‘lambda’ mean what Java, JavaScript, etc. mean by ‘lambda’? Where did lambdas come from? What were they originally for? What is their relationship to data abstraction?
In this session we will look into the history, the syntax and the uses of lambdas and the way in which lambda constructs in Java and other languages do (or do not) match the original construct introduced in lambda calculus.
This powerpoint presentation gives information regarding functions. Designed or grade 11 studens studying general mathematics 11. You can use this presentation to present your lessons in grade 11 general mathematics or even use this on your lesson in grade 10 mathematics about polynomial functions
Introduction to Neural Networks and Deep Learning from ScratchAhmed BESBES
If you're willing to understand how neural networks work behind the scene and debug the back-propagation algorithm step by step by yourself, this presentation should be a good starting point.
We'll cover elements on:
- the popularity of neural networks and their applications
- the artificial neuron and the analogy with the biological one
- the perceptron
- the architecture of multi-layer perceptrons
- loss functions
- activation functions
- the gradient descent algorithm
At the end, there will be an implementation FROM SCRATCH of a fully functioning neural net.
code: https://github.com/ahmedbesbes/Neural-Network-from-scratch
Function Programming in Scala.
A lot of my examples here comes from the book
Functional programming in Scala By Paul Chiusano and Rúnar Bjarnason, It is a good book, buy it.
This document discusses reasoning about laziness in Haskell. It explains that functions and data constructors don't evaluate their arguments until needed. This laziness allows separating producers and consumers efficiently. However, laziness can also cause problems like space leaks from unevaluated thunks. The document demonstrates techniques like using seq, bang patterns and strict data types to control evaluation order and avoid space leaks. It also discusses how to determine which arguments a function evaluates strictly.
Functional Programming by Examples using Haskellgoncharenko
The document discusses functional programming concepts in Haskell compared to traditional imperative languages like C++. It provides:
1) An example of quicksort implemented in both C++ and Haskell to illustrate the differences in approach and syntax between the two paradigms. The Haskell version is much more concise, using only 5 lines compared to 14 lines in C++.
2) Explanations of key functional programming concepts in Haskell including pure functions, recursion, pattern matching, and higher-order functions like map and fold.
3) Examples and definitions of commonly used Haskell functions and data types to summarize lists, sorting, and traversing elements - highlighting the more declarative style of functional programming.
This document discusses vector calculus concepts and operations involving the del operator (∇). It provides proofs and examples of:
1. Curl of the gradient of a scalar field is equal to zero.
2. The curl of the curl of a vector field is equal to the gradient of the divergence minus the Laplace operator.
3. The curl of the product of a scalar field and a vector field is equal to the scalar field times the curl of the vector field plus the cross product of the gradient of the scalar field and the vector field.
It also defines the Laplace operator and discusses its representation in spherical coordinates.
An overview of the Idris functional programming language. Idris has a number of interesting features such as dependent types, function totality checking and theorem proving.
The document discusses OTTER, a theorem prover that uses resolution style proofs. It provides background on OTTER, describing it as a resolution style theorem prover and discussing its clause representation and main strategies like restriction strategies, direction strategies, and look-ahead strategies. Examples are given of using OTTER to solve problems like the 15 puzzle and analyzing its complexity.
This document discusses various topics related to Haskell and functional programming:
1. It defines functions for adding, subtracting, and multiplying quaternions. However, quaternion multiplication is not commutative, so it cannot fully implement the Num typeclass.
2. It introduces the Maybe type for handling potential failure cases in functions like division or accessing the head of an empty list.
3. It defines a Tree type for representing binary trees and functions like mapping and calculating size.
4. It generalizes the idea of mapping with the Functor typeclass, showing instances for lists, Maybe, and trees. Homework involves making the Tree type into a binary search tree and adapting it for a key-value container
The document discusses functional programming and pattern matching. It provides examples of using pattern matching in functional programming to:
1. Match on algebraic data types like lists to traverse and operate on data in a recursive manner. Pattern matching allows adding new operations easily by adding new patterns.
2. Use pattern matching in variable declarations to destructure data like tuples and case class objects.
3. Perform pattern matching on function parameters to selectively apply different logic based on the patterns, like filtering even numbers from a list. Everything can be treated as values and expressions in functional programming.
This document provides an introduction to data structures and algorithms. It begins by discussing how to create programs through requirements analysis, design, coding, testing, and debugging. It then defines data structures as how data is organized and operated on, and explains that learning data structures and algorithms is important for performing tasks efficiently. Examples are provided to illustrate storing student data in an array and representing polynomials using arrays or linked lists. The document also covers searching arrays, recursive functions and binary search, abstract data types, performance analysis in terms of time and space complexity, and asymptotic notations for analyzing algorithms.
This document provides an overview of analytic functions in engineering mathematics. It defines analytic functions as functions whose derivatives exist in some neighborhood of a point, making them continuously differentiable. The Cauchy-Riemann equations are derived as necessary conditions for a function to be analytic. It also defines entire functions as analytic functions over the entire finite plane. Examples of entire functions include exponential, sine, cosine, and hyperbolic functions. The document discusses analyticity in both Cartesian and polar coordinates.
A Probabilistic Algorithm for Computation of Polynomial Greatest Common with ...mathsjournal
- The document presents a probabilistic algorithm for computing the polynomial greatest common divisor (PGCD) with smaller factors.
- It summarizes previous work on the subresultant algorithm for computing PGCD and discusses its limitations, such as not always correctly determining the variant τ.
- The new algorithm aims to determine τ correctly in most cases when given two polynomials f(x) and g(x). It does so by adding a few steps instead of directly computing the polynomial t(x) in the relation s(x)f(x) + t(x)g(x) = r(x).
The document discusses data structures and algorithms. It begins by introducing data structures and how they organize data to enable efficient operations. It provides examples of using arrays and linked lists to represent polynomials. The document then covers searching algorithms like binary search and recursive functions. It discusses abstract data types and how they separate an object's specification from its implementation. Finally, it covers analyzing algorithms, including analyzing their time and space complexity using asymptotic notations.
Similar to Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with minor corrections (20)
Hand Rolled Applicative User ValidationCode KataPhilip Schwarz
Could you use a simple piece of Scala validation code (granted, a very simplistic one too!) that you can rewrite, now and again, to refresh your basic understanding of Applicative operators <*>, <*, *>?
The goal is not to write perfect code showcasing validation, but rather, to provide a small, rough-and ready exercise to reinforce your muscle-memory.
Despite its grandiose-sounding title, this deck consists of just three slides showing the Scala 3 code to be rewritten whenever the details of the operators begin to fade away.
The code is my rough and ready translation of a Haskell user-validation program found in a book called Finding Success (and Failure) in Haskell - Fall in love with applicative functors.
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidPhilip Schwarz
The subject of this deck is the small Print[A] program in the following blog post by Noel Welsh: https://www.inner-product.com/posts/direct-style-effects/.
Keywords: "direct-style", "context function", "context functions", "algebraic effect", "algebraic effects", "scala", "effect system", "effect systems", "effect", "side effect", "composition", "fp", "functional programming"
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
For functions that can be defined both as an instance of a right fold and as an instance of a left fold, one may be more efficient than the other.
Let's look at the example of a function 'decimal' that converts a list of digits into the corresponding decimal number.
Erratum: it has been pointed out that it is possible to define the zip function using a right fold (see slide 5).
Folding Cheat Sheet #3 - third in a seriesPhilip Schwarz
This document summarizes the universal property of fold, which states that for finite lists the fold function is the unique function that satisfies its defining recursive equations. It provides examples of how common list functions like sum, product, length, concatenation (⧺) can be defined in terms of fold. It also notes that the universal property can be generalized to handle partial and infinite lists. Finally, it states that map, filter and fold form the "triad" or basic building blocks of functional programming.
Folding Cheat Sheet #2 - second in a seriesPhilip Schwarz
This document provides definitions and examples of foldr and foldl functions in functional programming. Foldr recursively applies a function from right to left, associating at each step. Foldl recursively applies a function from left to right, associating at each step. Examples are given applying foldr and foldl to a list with elements a0 through a3, using a function f and initial value b. Programmatic and mathematical definitions of foldr and foldl are also presented.
Folding Cheat Sheet #1 - first in a seriesPhilip Schwarz
This document provides examples of using fold functions to define recursive functions over natural numbers (Nat) and lists. It shows common patterns for defining recursive functions over Nat using foldn and over lists using foldr. Three examples are given of functions defined over Nat using foldn: addition, multiplication, and exponentiation. Three examples are also given of functions defined over lists using foldr: sum, length, and append.
Tagless Final Encoding - Algebras and Interpreters and also ProgramsPhilip Schwarz
Tagless Final Encoding - Algebras and Interpreters and also Programs - An introduction, through the work of Gabriel Volpe.
Slide deck home: http://fpilluminated.com/assets/tagless-final-encoding-algebras-interpreters-and-programs.html
A sighting of traverseFilter and foldMap in Practical FP in ScalaPhilip Schwarz
Slide deck home: http://fpilluminated.com/assets/sighting-of-scala-cats-traverseFilter-and-foldMap-in-practical-fp-in-scala.html.
Download PDF for perfect image quality.
A sighting of sequence function in Practical FP in ScalaPhilip Schwarz
Slide deck home: http://fpilluminated.com/assets/sighting-of-scala-cats-sequence-function-in-practical-fp-in-scala.html.
Download PDF for perfect image quality.
This talk was presented on Aug 3rd 2023 during the Scala in the City event a ITV in London https://www.meetup.com/scala-in-the-city/events/292844968/
Visit the following for a description, slideshow, all slides with transcript, pdf, github repo, and eventually a video recording: http://fpilluminated.com/assets/n-queens-combinatorial-puzzle-meets-cats.html
At the centre of this talk is the N-Queens combinatorial puzzle. The reason why this puzzle features in the Scala book and functional programming course by Martin Odersky (the language’s creator), is that such puzzles are a particularly suitable application area of 'for comprehensions'.
We’ll start by (re)acquainting ourselves with the puzzle, and seeing the role played in it by permutations. Next, we’ll see how, when wanting to visualise candidate puzzle solutions, Cats’ monoidal functions fold and foldMap are a great fit for combining images.
While we are all very familiar with the triad providing the bread, butter and jam of functional programming, i.e. map, filter and fold, not everyone knows about the corresponding functions in Cats’ monadic variant of the triad, i.e. mapM, filterM and foldM, which we are going to learn about next.
As is often the case in functional programming, the traverse function makes an appearance, and we shall grab the opportunity to point out the symmetry that exists in the interrelation of flatMap / foldMap / traverse and flatten / fold / sequence.
Armed with an understanding of foldM, we then look at how such a function can be used to implement an iterative algorithm for the N-Queens puzzle.
The talk ends by pointing out that the iterative algorithm is smarter than the recursive one, because it ‘remembers’ where it has already placed previous queens.
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Philip Schwarz
Kleisli composition, flatMap, join, map, unit. A study/memory aid, to help learn/recall their implementation/interrelation.
Version 2, updated for Scala 3
Nat, List and Option Monoids -from scratch -Combining and Folding -an examplePhilip Schwarz
Nat, List and Option Monoids, from scratch. Combining and Folding: an example.
This is a new version of the original which has some cosmetic changes and a new 7th slide which only differs from slide 6 in that it defines the fold function in terms of the foldRight function.
Code: https://github.com/philipschwarz/nat-list-and-option-monoids-from-scratch-combining-and-folding-an-example
Nat, List and Option Monoids -from scratch -Combining and Folding -an examplePhilip Schwarz
Nat, List and Option Monoids, from scratch. Combining and Folding: an example.
Code: https://github.com/philipschwarz/nat-list-and-option-monoids-from-scratch-combining-and-folding-an-example
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...Philip Schwarz
When I posted the deck for Part 1 to the Scala users forum, Odd Möller linked to a paper titled "The Genuine Sieve of Eratosthenes", which speaks of the Unfaithful Sieve.
Part 2 is based on that paper and on Richard Bird's faithful Haskell implementation of the Sieve, which we translate into Scala.
Scala code for Richard Bird's infinite primes Haskell program: https://github.com/philipschwarz/sieve-of-eratosthenes-part-2-scala
Jordan Peterson - The pursuit of meaning and related ethical axiomsPhilip Schwarz
I have only recently become aware of the work of Jordan Peterson. Because I am finding it so interesting, I hope that the following small collection of excerpts from some of his writings and speeches might entice any fellow latecomers to find out more about his work. See below for my own summary of some of the subjects touched upon in these slides.
Download for best results.
Takashi Kobayashi and Hironori Washizaki, "SWEBOK Guide and Future of SE Education," First International Symposium on the Future of Software Engineering (FUSE), June 3-6, 2024, Okinawa, Japan
OpenMetadata Community Meeting - 5th June 2024OpenMetadata
The OpenMetadata Community Meeting was held on June 5th, 2024. In this meeting, we discussed about the data quality capabilities that are integrated with the Incident Manager, providing a complete solution to handle your data observability needs. Watch the end-to-end demo of the data quality features.
* How to run your own data quality framework
* What is the performance impact of running data quality frameworks
* How to run the test cases in your own ETL pipelines
* How the Incident Manager is integrated
* Get notified with alerts when test cases fail
Watch the meeting recording here - https://www.youtube.com/watch?v=UbNOje0kf6E
SOCRadar's Aviation Industry Q1 Incident Report is out now!
The aviation industry has always been a prime target for cybercriminals due to its critical infrastructure and high stakes. In the first quarter of 2024, the sector faced an alarming surge in cybersecurity threats, revealing its vulnerabilities and the relentless sophistication of cyber attackers.
SOCRadar’s Aviation Industry, Quarterly Incident Report, provides an in-depth analysis of these threats, detected and examined through our extensive monitoring of hacker forums, Telegram channels, and dark web platforms.
Preparing Non - Technical Founders for Engaging a Tech AgencyISH Technologies
Preparing non-technical founders before engaging a tech agency is crucial for the success of their projects. It starts with clearly defining their vision and goals, conducting thorough market research, and gaining a basic understanding of relevant technologies. Setting realistic expectations and preparing a detailed project brief are essential steps. Founders should select a tech agency with a proven track record and establish clear communication channels. Additionally, addressing legal and contractual considerations and planning for post-launch support are vital to ensure a smooth and successful collaboration. This preparation empowers non-technical founders to effectively communicate their needs and work seamlessly with their chosen tech agency.Visit our site to get more details about this. Contact us today www.ishtechnologies.com.au
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Crescat
Crescat is industry-trusted event management software, built by event professionals for event professionals. Founded in 2017, we have three key products tailored for the live event industry.
Crescat Event for concert promoters and event agencies. Crescat Venue for music venues, conference centers, wedding venues, concert halls and more. And Crescat Festival for festivals, conferences and complex events.
With a wide range of popular features such as event scheduling, shift management, volunteer and crew coordination, artist booking and much more, Crescat is designed for customisation and ease-of-use.
Over 125,000 events have been planned in Crescat and with hundreds of customers of all shapes and sizes, from boutique event agencies through to international concert promoters, Crescat is rigged for success. What's more, we highly value feedback from our users and we are constantly improving our software with updates, new features and improvements.
If you plan events, run a venue or produce festivals and you're looking for ways to make your life easier, then we have a solution for you. Try our software for free or schedule a no-obligation demo with one of our product specialists today at crescat.io
DDS Security Version 1.2 was adopted in 2024. This revision strengthens support for long runnings systems adding new cryptographic algorithms, certificate revocation, and hardness against DoS attacks.
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j
Dr. Jesús Barrasa, Head of Solutions Architecture for EMEA, Neo4j
Découvrez les dernières innovations de Neo4j, et notamment les dernières intégrations cloud et les améliorations produits qui font de Neo4j un choix essentiel pour les développeurs qui créent des applications avec des données interconnectées et de l’IA générative.
GraphSummit Paris - The art of the possible with Graph TechnologyNeo4j
Sudhir Hasbe, Chief Product Officer, Neo4j
Join us as we explore breakthrough innovations enabled by interconnected data and AI. Discover firsthand how organizations use relationships in data to uncover contextual insights and solve our most pressing challenges – from optimizing supply chains, detecting fraud, and improving customer experiences to accelerating drug discoveries.
Zoom is a comprehensive platform designed to connect individuals and teams efficiently. With its user-friendly interface and powerful features, Zoom has become a go-to solution for virtual communication and collaboration. It offers a range of tools, including virtual meetings, team chat, VoIP phone systems, online whiteboards, and AI companions, to streamline workflows and enhance productivity.
Artificia Intellicence and XPath Extension FunctionsOctavian Nadolu
The purpose of this presentation is to provide an overview of how you can use AI from XSLT, XQuery, Schematron, or XML Refactoring operations, the potential benefits of using AI, and some of the challenges we face.
Graspan: A Big Data System for Big Code AnalysisAftab Hussain
We built a disk-based parallel graph system, Graspan, that uses a novel edge-pair centric computation model to compute dynamic transitive closures on very large program graphs.
We implement context-sensitive pointer/alias and dataflow analyses on Graspan. An evaluation of these analyses on large codebases such as Linux shows that their Graspan implementations scale to millions of lines of code and are much simpler than their original implementations.
These analyses were used to augment the existing checkers; these augmented checkers found 132 new NULL pointer bugs and 1308 unnecessary NULL tests in Linux 4.4.0-rc5, PostgreSQL 8.3.9, and Apache httpd 2.2.18.
- Accepted in ASPLOS ‘17, Xi’an, China.
- Featured in the tutorial, Systemized Program Analyses: A Big Data Perspective on Static Analysis Scalability, ASPLOS ‘17.
- Invited for presentation at SoCal PLS ‘16.
- Invited for poster presentation at PLDI SRC ‘16.
Launch Your Streaming Platforms in MinutesRoshan Dwivedi
The claim of launching a streaming platform in minutes might be a bit of an exaggeration, but there are services that can significantly streamline the process. Here's a breakdown:
Pros of Speedy Streaming Platform Launch Services:
No coding required: These services often use drag-and-drop interfaces or pre-built templates, eliminating the need for programming knowledge.
Faster setup: Compared to building from scratch, these platforms can get you up and running much quicker.
All-in-one solutions: Many services offer features like content management systems (CMS), video players, and monetization tools, reducing the need for multiple integrations.
Things to Consider:
Limited customization: These platforms may offer less flexibility in design and functionality compared to custom-built solutions.
Scalability: As your audience grows, you might need to upgrade to a more robust platform or encounter limitations with the "quick launch" option.
Features: Carefully evaluate which features are included and if they meet your specific needs (e.g., live streaming, subscription options).
Examples of Services for Launching Streaming Platforms:
Muvi [muvi com]
Uscreen [usencreen tv]
Alternatives to Consider:
Existing Streaming platforms: Platforms like YouTube or Twitch might be suitable for basic streaming needs, though monetization options might be limited.
Custom Development: While more time-consuming, custom development offers the most control and flexibility for your platform.
Overall, launching a streaming platform in minutes might not be entirely realistic, but these services can significantly speed up the process compared to building from scratch. Carefully consider your needs and budget when choosing the best option for you.
When deliberating between CodeIgniter vs CakePHP for web development, consider their respective strengths and your project requirements. CodeIgniter, known for its simplicity and speed, offers a lightweight framework ideal for rapid development of small to medium-sized projects. It's praised for its straightforward configuration and extensive documentation, making it beginner-friendly. Conversely, CakePHP provides a more structured approach with built-in features like scaffolding, authentication, and ORM. It suits larger projects requiring robust security and scalability. Ultimately, the choice hinges on your project's scale, complexity, and your team's familiarity with the frameworks.
Codeigniter VS Cakephp Which is Better for Web Development.pdf
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with minor corrections
1. See how recursive functions and structural induction relate to recursive datatypes
Follow along as the fold abstraction is introduced and explained
Watch as folding is used to simplify the definition of recursive functions over recursive datatypes
Part 1 - through the work of
Folding Unfolded
Polyglot FP for Fun and Profit
Haskell and Scala
Graham Hutton
@haskellhutt
@philip_schwarzslides by https://www.slideshare.net/pjschwarz
Richard Bird
http://www.cs.ox.ac.uk/people/richard.bird/
2. Richard Bird
@philip_schwarz
This slide deck is almost entirely centered on material from Richard Bird’s fantastic book, Introduction to Functional
Programming using Haskell.
I hope he’ll forgive me for relying so heavily on his work, but I couldn’t resist using extensive excerpts from his compelling book
to introduce and explain the concept of folding.
http://www.cs.ox.ac.uk/people/richard.bird/
https://en.wikipedia.org/wiki/Richard_Bird_(computer_scientist)
3. 3.1 Natural Numbers
The natural numbers are the numbers 0, 1, 2 and so on, used for counting. The type 𝑵𝒂𝒕 is introduced by the declaration
𝐝𝐚𝐭𝐚 𝑵𝒂𝒕 = 𝒁𝒆𝒓𝒐 | 𝑺𝒖𝒄𝒄 𝑵𝒂𝒕
𝑵𝒂𝒕 is our first example of a recursive datatype declaration. The definition says that Zero is a value of 𝑵𝒂𝒕, and that Succ 𝑛 is a
value of 𝑵𝒂𝒕 whenever 𝑛 is. In particular, the constructor 𝑺𝒖𝒄𝒄 (short for ‘successor’), has type 𝑵𝒂𝒕 → 𝑵𝒂𝒕. For example, each of
𝒁𝒆𝒓𝒐, 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐, 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐)
is an element of 𝑵𝒂𝒕. As an element of 𝑵𝒂𝒕 the number 7 would be represented by
𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐))))))
Every natural number is represented by a unique value of 𝑵𝒂𝒕. On the other hand, not every value of 𝑵𝒂𝒕 represents a well-
defined natural number. In fact 𝑵𝒂𝒕 also contains the values ⊥, 𝑺𝒖𝒄𝒄 ⊥, 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 ⊥), and so on. These additional values will
be discussed later.
Let us see how to program the basic arithmetic and comparison operations on 𝑵𝒂𝒕. Addition can be defined by
+ ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 + 𝒁𝒆𝒓𝒐 = 𝑚
𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛
This is a recursive definition, defining + by pattern matching on the second argument. Since every element of 𝑵𝒂𝒕, apart for ⊥, is
either 𝒁𝒆𝒓𝒐 or of the form Succ 𝑛, where 𝑛 is an element of 𝑵𝒂𝒕, the two patterns in the equations for + are disjoint and cover all
numbers apart from ⊥.
… Richard Bird
4. Here is how 𝒁𝒆𝒓𝒐 + 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐) would be evaluated:
𝒁𝒆𝒓𝒐 + 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐)
= { second equation for +, i.e. 𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛 }
𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 + 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐
= { second equation for +, i.e. 𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛 }
𝑺𝒖𝒄𝒄 𝑺𝒖𝒄𝒄 (𝒁𝒆𝒓𝒐 + 𝒁𝒆𝒓𝒐)
= { first equation for +, i.e. 𝑚 + 𝒁𝒆𝒓𝒐 = 𝑚 }
𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐)
…it is not a practical proposition to introduce natural numbers through the datatype 𝑵𝒂𝒕: arithmetic would be just too inefficient.
In particular, calculating 𝑚 + n would require (𝑛 + 1) evaluation steps. On the other hand, counting on your fingers is a good way
to understand addition.
Given +, we can define ×:
(×) ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 × 𝒁𝒆𝒓𝒐 = 𝒁𝒆𝒓𝒐
𝑚 × 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 × 𝑛 + 𝑚
Given ×, we can define exponentiation (↑) by
(↑) ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 ↑ 𝒁𝒆𝒓𝒐 = 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐
𝑚 ↑ 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 ↑ 𝑛 × 𝑚
…
Richard Bird
5. On the next slide we show the definitions of +, ×,
and ↑ again, and have a go at implementing the
three operations in Scala, together with some tests.
7. The remaining arithmetic operation common to all numbers is subtraction (−). However, subtraction is a partial operation on
natural numbers. The definition is
− ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 − 𝒁𝒆𝒓𝒐 = 𝑚
𝑺𝒖𝒄𝒄 𝑚 − 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 − 𝑛
This definition uses pattern matching on both arguments; taken together, the patterns are disjoint but not exhaustive. For
example,
𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 − 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐)
= { second equation for −, i.e. 𝑺𝒖𝒄𝒄 𝑚 − 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 − 𝑛 }
𝒁𝒆𝒓𝒐 − 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐
= { case exhaustion }
⊥
The hint ‘case exhaustion’ in the last step indicates that no equation for − has a pattern that matches (𝒁𝒆𝒓𝒐 − 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐).
More generally, 𝑚 − 𝑛 = ⊥ if 𝑚 < 𝑛. The partial nature of subtraction on the natural numbers is the prime motivation for
introducing the integer numbers; over the integers, − is a total operation.
...Finally, here are two more examples of programming with 𝑵𝒂𝒕. The factorial and Fibonacci functions are defined by
𝑓𝑎𝑐𝑡 ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑓𝑎𝑐𝑡 𝒁𝒆𝒓𝒐 = 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐
𝑓𝑎𝑐𝑡 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑛 × 𝑓𝑎𝑐𝑡 𝑛
𝑓𝑖𝑏 ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑓𝑖𝑏 𝒁𝒆𝒓𝒐 = 𝒁𝒆𝒓𝒐
𝑓𝑖𝑏 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 = 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐
𝑓𝑖𝑏 𝑺𝒖𝒄𝒄 𝑺𝒖𝒄𝒄 𝑛 = 𝑓𝑖𝑏 𝑺𝒖𝒄𝒄 𝑛 + 𝑓𝑖𝑏 𝑛 Richard Bird
8. See the next slide for a Scala
implementation of the − operation.
9. − ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 − 𝒁𝒆𝒓𝒐 = 𝑚
𝑺𝒖𝒄𝒄 𝑚 − 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 − 𝑛
val `(-)`: Nat => Nat => Nat =
m => n => (m,n) match {
case (_,Zero) => m
case (Succ(x),Succ(y)) => x - y
}
; assert(0 - 0 == 0) ; assert(Zero - Zero == Zero)
; assert(1 - 0 == 1) ; assert(Succ(Zero) - Zero == Succ(Zero))
; assert(5 - 3 == 2) ; assert(Succ(Succ(Succ(Succ(Succ(Zero))))) - Succ(Succ(Succ(Zero))) == Succ(Succ(Zero)))
; assert(3 - 5 == -2) ; assert(
Try {
Succ(Succ(Succ(Zero))) - Succ(Succ(Succ(Succ(Succ(Zero)))))
}.toString.startsWith(
"Failure(scala.MatchError: (Zero,Succ(Succ(Zero)))"
)
)
No equation for − has a pattern that matches (𝒁𝒆𝒓𝒐 − 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐).
Similarly for (𝒁𝒆𝒓𝒐 − 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐)), (𝒁𝒆𝒓𝒐 − 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄(𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐))), etc.
More generally, 𝑚 − 𝑛 = ⊥ if 𝑚 < 𝑛.
𝑚 − 𝑛 throws scala.MatchError if 𝑚 < 𝑛.
10. @philip_schwarz
On the next slide we show the definitions of 𝑓𝑎𝑐𝑡,
and 𝑓𝑖𝑏 again, and have a go at implementing the
two functions in Scala, together with some tests.
12. 3.1.1 Partial numbers
Let us now return to the point about there being extra values in 𝑵𝒂𝒕. The values
⊥, 𝑺𝒖𝒄𝒄 ⊥, 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 ⊥), …
are all different and each is also a member of 𝑵𝒂𝒕. That they exist is a consequence of three facts:
i. ⊥ is an element of 𝑵𝒂𝒕 because every datatype declaration introduces at least one extra value, the undefined value of
the type.
ii. constructor functions of a datatype are assumed to be nonstrict
iii. 𝑺𝒖𝒄𝒄 𝑛 is an element of Nat, whenever 𝑛 is
To appreciate why these extra values are different from one another, suppose we define 𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑 ∷ 𝑵𝒂𝒕 by the equation
𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑 = 𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑. Then
? 𝒁𝒆𝒓𝒐 < 𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑
{Interrupted!}
? 𝒁𝒆𝒓𝒐 < 𝑺𝒖𝒄𝒄 𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑
𝑇𝑟𝑢𝑒
? 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 < 𝑺𝒖𝒄𝒄 𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑
{Interrupted!}
? 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 < 𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑)
𝑇𝑟𝑢𝑒
One can interpret the extra values in the following way: ⊥ corresponds to the natural number about which there is
absolutely no information; 𝑺𝒖𝒄𝒄 ⊥ to the natural number about which the only information is that it is greater than 𝒁𝒆𝒓𝒐;
𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 ⊥) to the natural number about which the only information is that it is greater than 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐; and so on.
Richard Bird
13. There is also one further value of 𝑵𝒂𝒕, namely the ‘infinite’ number:
𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 … )))
This number can be defined by
𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦 ∷ 𝑵𝒂𝒕
𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦 = 𝑺𝒖𝒄𝒄 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦
It is different from all the other numbers, because it is the only number 𝑥 for which 𝑺𝒖𝒄𝒄 𝑚 < 𝑥 returns 𝑻𝒓𝒖𝒆 for all finite
numbers 𝑚. In this sense, 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦 is the largest element of 𝑵𝒂𝒕. If we request the value of 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦, then we obtain
? 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦
𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 {𝐼𝑛𝑡𝑒𝑟𝑟𝑢𝑝𝑡𝑒𝑑!}
The number 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦 satisfies other properties, in particular 𝑛 + 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦 = 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦, for all numbers 𝑛. The dual equation
𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦 + 𝑛 = 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦 holds only for finite numbers 𝑛. We will see how to prove assertions such as these in the next
section.
To summarise this discussion, we can divide the values of 𝑵𝒂𝒕 into three classes:
• The finite numbers, those that correspond to well-defined natural numbers.
• The partial numbers, ⊥, 𝑺𝒖𝒄𝒄 ⊥, and so on.
• The infinite numbers, of which there is just one, namely 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦.
We will see that this classification holds true of all recursive types. There will be the finite elements of the type, the partial
elements, and the infinite elements. Although the infinite natural number is not of much use, the same is not true of the
infinite values of other datatypes.
… Richard Bird
14. Note that when in this slide deck we mention the concepts of ⊥
and 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦, it is mainly in a Haskell context, as we did in the last
two slides. In particular, we won’t be modelling ⊥ and 𝑖𝑛𝑓𝑖𝑛𝑖𝑡𝑦 in
any of the Scala code you’ll see throughout the deck.
15. 3.2 Induction
In order to reason about the properties of recursively defined functions over a recursive datatype, we can appeal to a principle of
structural induction. In the case of 𝑵𝒂𝒕, the principle of structural induction can be defined as follows: In order to show that some
property 𝑃(𝑛) holds for each finite number 𝑛 of 𝑵𝒂𝒕, it is sufficient to show:
Case (𝒁𝒆𝒓𝒐). That 𝑃(𝒁𝒆𝒓𝒐) holds.
Case (𝑺𝒖𝒄𝒄 𝑛). That if 𝑃(𝑛) holds, then 𝑃(𝑺𝒖𝒄𝒄 𝑛) holds also.
Induction is valid for the same reason that recursive definitions are valid: every finite number is either 𝒁𝒆𝒓𝒐 or of the form
𝑺𝒖𝒄𝒄 𝑛, where 𝑛 is a finite number. If we prove the first case, then we have shown that the property is true for 𝒁𝒆𝒓𝒐; If we also
prove the second case, then we have shown that the property is true for 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐, since it is true for 𝒁𝒆𝒓𝒐. But now, by the
same argument, it is true for 𝑺𝒖𝒄𝒄 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 , and so on.
The principle needs to be extended if we want to assert that some proposition is true for all elements of 𝑵𝒂𝒕, but we postpone
discussion of this point for the following section.
As an example, let’s prove that 𝒁𝒆𝒓𝒐 + 𝑛 = 𝑛 for all finite numbers 𝑛. Recall that + is defined by
𝑚 + 𝒁𝒆𝒓𝒐 = 𝑚
𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛
The first equation asserts that 𝒁𝒆𝒓𝒐 is a right unit of +. In general, 𝑒 is a left unit of ⊕ if 𝑒 ⊕ 𝑥 = 𝑥 for all 𝑥, and a right unit
of 𝑥 if 𝑥 ⊕ 𝑒 = 𝑥 for all 𝑥. If 𝑒 is both a left unit and a right unit of an operator ⊕, then it is called the unit of ⊕. The
terminology is appropriate since only one value can be both a left and right unit. So, by proving that 𝒁𝒆𝒓𝒐 is a left unit , we
have proved that 𝒁𝒆𝒓𝒐 is the unit of +.
Richard Bird
16. Proof. The proof is by induction on 𝑛. More precisely, we take for 𝑃(𝑛) the assertion that 𝒁𝒆𝒓𝒐 + 𝑛 = 𝑛. This equation is
referred to as the induction hypothesis.
Case (𝒁𝒆𝒓𝒐). We have to show 𝒁𝒆𝒓𝒐 + 𝒁𝒆𝒓𝒐 = 𝒁𝒆𝒓𝒐, which is immediate from the first equation defining +.
Case (𝑺𝒖𝒄𝒄 𝑛). We have to show that 𝒁𝒆𝒓𝒐 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑛, which we do by simplifying the left-hand expression:
𝒁𝒆𝒓𝒐 + 𝑺𝒖𝒄𝒄 𝑛
= { second equation for +, i.e. 𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛 }
𝑺𝒖𝒄𝒄 (𝒁𝒆𝒓𝒐 + 𝑛)
= { induction hypothesis}
𝑺𝒖𝒄𝒄 𝑛
☐
This example shows the format we will use for inductive proofs, laying out each case separately and using a ☐ to mark the end.
The very last step made use of the induction hypothesis, which is allowed by the way induction works.
…
3.2.1 Full Induction
In the form given above, the induction principle for 𝑵𝒂𝒕 suffices only to prove properties of the finite members of 𝑵𝒂𝒕. If we
want to show that a property 𝑃 also hold for every partial number, then we have to prove three things:
Case (⊥). That 𝑃(⊥) holds.
Case (𝒁𝒆𝒓𝒐). That 𝑃(𝒁𝒆𝒓𝒐) holds.
Case (𝑺𝒖𝒄𝒄 𝑛). That if 𝑃(𝑛) holds, then 𝑃(𝑺𝒖𝒄𝒄 𝑛) holds also.
We can omit the second case, but then we can conclude only that 𝑃(𝑛) holds for every partial number. The reason the
principle is valid is that is that every partial number is either ⊥ or of the form 𝑺𝒖𝒄𝒄 𝑛 for some partial number 𝑛. Richard Bird
17. To illustrate, let us prove the somewhat counterintuitive result that 𝑚 + 𝑛 = 𝑛 for all numbers 𝑚 and all partial numbers 𝑛.
Proof. The proof is by partial number induction on 𝑛.
Case (⊥). The equation 𝑚 + ⊥ = ⊥ follows at once by case exhaustion in the definition of +. That is, ⊥ does not match either of
the patterns 𝒁𝒆𝒓𝒐 or 𝑺𝒖𝒄𝒄 𝑛.
Case (𝑺𝒖𝒄𝒄 𝑛). For the left-hand side, we reason
𝑚 + 𝑺𝒖𝒄𝒄 𝑛
= { second equation for +, i.e. 𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛 }
𝑺𝒖𝒄𝒄 (𝑚 + 𝑛)
= { induction hypothesis}
𝑺𝒖𝒄𝒄 𝑛
Since the right-hand side is also 𝑺𝒖𝒄𝒄 𝑛, we are done.
3.2.2 Program synthesis
In the proofs above we defined some functions and then used induction to prove a certain property. We can also view
induction as a way to synthesise definitions of functions so that they satisfy the properties we want.
Let us illustrate with a simple example. Suppose we specify subtraction of natural numbers by the condition
𝑚 + 𝑛 − 𝑛 = 𝑚
for all 𝑚 and 𝑛. The specification does not give a constructive definition of − , merely a property that it has to satisfy.
However, we can do an induction proof on 𝑛 of the equation above, but view the calculation as a way of generating a suitable
definition of − .
Richard Bird
18. Unlike previous proofs, we reason with the equation as a whole, since simplification of both sides independently is not
possible if we do not know what all the rules of simplification are.
Case (𝒁𝒆𝒓𝒐). We reason
𝑚 + 𝒁𝒆𝒓𝒐 − 𝒁𝒆𝒓𝒐 = 𝑚
≡ { first equation for +, i.e. 𝑚 + 𝒁𝒆𝒓𝒐 = 𝑚 }
𝑚 − 𝒁𝒆𝒓𝒐 = 𝑚
Hence we can take 𝑚 − 𝒁𝒆𝒓𝒐 = 𝑚 to satisfy the case. The symbol ≡ is used to separate steps of the calculation since we are
calculating with mathematical assertions, not with values of a datatype.
Case (𝑺𝒖𝒄𝒄 𝑛). We reason
𝑚 + 𝑺𝒖𝒄𝒄 𝑛 − 𝑺𝒖𝒄𝒄 𝑛 = 𝑚
≡ { second equation for +, i.e. 𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛 }
𝑺𝒖𝒄𝒄 𝑚 + 𝑛 − 𝑺𝒖𝒄𝒄 𝑛 = 𝑚
≡ { hypothesis 𝑚 + 𝑛 − 𝑛 = 𝑚}
𝑺𝒖𝒄𝒄 𝑚 + 𝑛 − 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 + 𝑛 − 𝑛
Replacing 𝑚 + 𝑛 in the last equation by 𝑚, we can take 𝑺𝒖𝒄𝒄 𝑚 − 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 − 𝑛 to satisfy the case. Hence we have derived
𝑚 − 𝒁𝒆𝒓𝒐 = 𝑚
𝑺𝒖𝒄𝒄 𝑚 − 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 − 𝑛
This is the program for − seen earlier.
Richard Bird
19. After that look at structural induction, it is finally time to
see how Richard Bird introduces the concept of folding.
20. 3.3 The fold function
Many of the recursive definitions seen so far have a common pattern, exemplified by the following definition of a function 𝑓:
𝑓 ∷ 𝑵𝒂𝒕 → 𝐴
𝑓 𝒁𝒆𝒓𝒐 = 𝑐
𝑓 𝑺𝒖𝒄𝒄 𝑛 = ℎ 𝑓 𝑛
Here, 𝐴 is some type, 𝑐 is an element of 𝐴, and ℎ ∷ 𝐴 → 𝐴 . Observe that 𝑓 works by taking an element of 𝑵𝒂𝒕 and replacing
𝒁𝒆𝒓𝒐 by 𝑐 and 𝑺𝒖𝒄𝒄 by ℎ. For example, 𝑓 takes
𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 (𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐)) to ℎ (ℎ (ℎ 𝑐))
The two equations for 𝑓 can be captured in terms of a single function, 𝑓𝑜𝑙𝑑𝑛, called the 𝑓𝑜𝑙𝑑 function for 𝑵𝒂𝒕. The definition is
𝑓𝑜𝑙𝑑𝑛 ∷ 𝛼 → 𝛼 → 𝛼 → 𝑵𝒂𝒕 → 𝛼
𝑓𝑜𝑙𝑑𝑛 ℎ 𝑐 𝒁𝒆𝒓𝒐 = 𝑐
𝑓𝑜𝑙𝑑𝑛 ℎ 𝑐 𝑺𝒖𝒄𝒄 𝑛 = ℎ 𝑓𝑜𝑙𝑑𝑛 ℎ 𝑐 𝑛
In particular, we have
𝑚 + 𝑛 = 𝑓𝑜𝑙𝑑𝑛 𝑺𝒖𝒄𝒄 𝑚 𝑛
𝑚 × 𝑛 = 𝑓𝑜𝑙𝑑𝑛 + 𝑚 𝒁𝒆𝒓𝒐 𝑛
𝑚 ↑ 𝑛 = 𝑓𝑜𝑙𝑑𝑛 × 𝑚 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 𝑛
It follows also that the identity function 𝑖𝑑 on 𝑵𝒂𝒕 satisfies 𝑖𝑑 = 𝑓𝑜𝑙𝑑𝑛 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐. A suitable 𝑓𝑜𝑙𝑑 function can be defined for
every recursive type, and we will see other 𝑓𝑜𝑙𝑑 functions in the following chapters.
Richard Bird
21. + ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 + 𝒁𝒆𝒓𝒐 = 𝑚
𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛
(×) ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 × 𝒁𝒆𝒓𝒐 = 𝒁𝒆𝒓𝒐
𝑚 × 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 × 𝑛 + 𝑚
(↑) ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 ↑ 𝒁𝒆𝒓𝒐 = 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐
𝑚 ↑ 𝑺𝒖𝒄𝒄 𝑛 = 𝑚 ↑ 𝑛 × 𝑚
+ ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 + 𝑛 = 𝑓𝑜𝑙𝑑𝑛 𝑺𝒖𝒄𝒄 𝑚 𝑛
(×) ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 × 𝑛 = 𝑓𝑜𝑙𝑑𝑛 + 𝑚 𝒁𝒆𝒓𝒐 𝑛
(↑) ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 ↑ 𝑛 = 𝑓𝑜𝑙𝑑𝑛 × 𝑚 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 𝑛
𝑓𝑜𝑙𝑑𝑛 ∷ 𝛼 → 𝛼 → 𝛼 → 𝑵𝒂𝒕 → 𝛼
𝑓𝑜𝑙𝑑𝑛 ℎ 𝑐 𝒁𝒆𝒓𝒐 = 𝑐
𝑓𝑜𝑙𝑑𝑛 ℎ 𝑐 𝑺𝒖𝒄𝒄 𝑛 = ℎ 𝑓𝑜𝑙𝑑𝑛 ℎ 𝑐 𝑛
@philip_schwarz
Just to reinforce the ideas on the previous slide, here are the original definitions
of +, × and ↑, and next to them, the new definitions in terms of 𝑓𝑜𝑙𝑑𝑛.
And the next slide is the same but in terms of Scala code.
22. val `(+)`: Nat => Nat => Nat =
m => {
case Zero => m
case Succ(n) => Succ(m + n)
}
val `(×)`: Nat => Nat => Nat =
m => {
case Zero => Zero
case Succ(n) => (m × n) + m
}
val `(↑)`: Nat => Nat => Nat =
m => {
case Zero => Succ(Zero)
case Succ(n) => (m ↑ n) × m
}
val `(×)`: Nat => Nat => Nat =
m => n => foldn((x:Nat) => x + m, Zero, n)
def foldn[A](h: A => A, c: A, n: Nat): A =
n match {
case Zero => c
case Succ(n) => h(foldn(h,c,n))
}
val `(↑)`: Nat => Nat => Nat =
m => n => foldn((x:Nat) => x × m, Succ(Zero), n)
val `(+)`: Nat => Nat => Nat =
m => n => foldn(Succ,m,n)
23. In the examples above, each instance of 𝑓𝑜𝑙𝑑𝑛 also returned an element of 𝑵𝒂𝒕. In the following two examples, 𝑓𝑜𝑙𝑑𝑛
returns an element of (𝑵𝒂𝒕, 𝑵𝒂𝒕):
𝑓𝑎𝑐𝑡 ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑓𝑎𝑐𝑡 = 𝑠𝑛𝑑 Š 𝑓𝑜𝑙𝑑𝑛 𝑓 (𝒁𝒆𝒓𝒐, 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐)
where 𝑓 𝑚, 𝑛 = (𝑺𝒖𝒄𝒄 𝑚, 𝑺𝒖𝒄𝒄 (𝑚) × 𝑛)
𝑓𝑖𝑏 ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑓𝑖𝑏 = 𝑓𝑠𝑡 Š 𝑓𝑜𝑙𝑑𝑛 𝑔 (𝒁𝒆𝒓𝒐, 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐)
where 𝑔 𝑚, 𝑛 = (𝑛, 𝑚 + 𝑛)
The function 𝑓𝑎𝑐𝑡 computes the factorial function and function 𝑓𝑖𝑏 computes the Fibonacci function. Each program works by first
computing a more general result, namely an element of (𝑵𝒂𝒕, 𝑵𝒂𝒕), and then extracts the required result. In fact,
𝑓𝑜𝑙𝑑𝑛 𝑓 𝒁𝒆𝒓𝒐, 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 𝑛 = 𝑛, 𝑓𝑎𝑐𝑡 𝑛
𝑓𝑜𝑙𝑑𝑛 𝑔 𝒁𝒆𝒓𝒐, 𝑺𝒖𝒄𝒄 𝒁𝒆𝒓𝒐 𝑛 = 𝑓𝑖𝑏 𝑛, 𝑓𝑖𝑏 𝑺𝒖𝒄𝒄 𝑛
These equations can be proved by induction. The program for 𝑓𝑖𝑏 is more efficient than a direct recursive definition. The
recursive program requires an exponential number of + operations, while the program above requires only a linear number. We
will discuss efficiency in more detail in chapter 7, where the programming technique that led to the invention of the new program
for 𝑓𝑖𝑏 will be studied in a more general setting.
There are two advantages of writing recursive definitions in terms of 𝑓𝑜𝑙𝑑𝑛. Firstly, the definition is shorter; rather than having to
write down two equations, we have only to write down one. Secondly, it is possible to prove general properties of 𝑓𝑜𝑙𝑑𝑛 and use
them to prove properties of specific instantiations. In other words, rather than having to write down many induction proofs, we
have only to write down one.
Richard Bird
24. @philip_schwarz
The next slide shows the original definitions of the factorial and Fibonacci
functions, and next to them, the new definitions in terms of 𝑓𝑜𝑙𝑑𝑛.
And the slide after that is the same but in terms of Scala code.
26. def fact(n: Nat): Nat = {
def snd(pair: (Nat, Nat)): Nat =
pair match { case (_,n) => n }
def f(pair: (Nat, Nat)): (Nat, Nat) =
pair match { case (m,n) => (Succ(m), Succ(m) × n) }
snd( foldn(f, (Zero, Succ(Zero)), n) )
}
def fib(n: Nat): Nat = {
def fst(pair: (Nat, Nat)): Nat =
pair match { case (n,_) => n }
def g(pair: (Nat, Nat)): (Nat, Nat) =
pair match { case (m,n) => (n, m + n) }
fst( foldn(g, (Zero, Succ(Zero)), n) )
}
val fact: Nat => Nat = {
case Zero => Succ(Zero)
case Succ(n) => Succ(n) × fact(n)
}
val fib: Nat => Nat = {
case Zero => Zero
case Succ(Zero) => Succ(Zero)
case Succ(Succ(n)) => fib(Succ(n)) + fib(n)
}
def foldn[A](h: A => A, c: A, n: Nat): A =
n match {
case Zero => c
case Succ(n) => h(foldn(h,c,n))
}
27. Now let’s have a very quick look at the
datatype for lists, and at induction over lists.
28. 4.1.1 Lists as a datatype
A list can be constructed from scratch by starting with the empty list and successively adding elements one by one. One can add
elements to the front of the list, or to the rear, or to somewhere in the middle. In the following datatype declaration, nonempty
lists are constructed by adding elements to the front of the list:
𝐝𝐚𝐭𝐚 𝑳𝒊𝒔𝒕 𝛼 = 𝑵𝒊𝒍 | 𝑪𝒐𝒏𝒔 𝛼 (𝑳𝒊𝒔𝒕 𝛼)
…The constructor (short for ‘construct’ – the name goes back to the programming language LISP) adds an element to the front of
the list. For example, the list 1,2,3 would be represented as the following element of 𝑳𝒊𝒔𝒕 𝑰𝒏𝒕:
𝑪𝒐𝒏𝒔 1 (𝑪𝒐𝒏𝒔 2 (𝑪𝒐𝒏𝒔 3 𝑵𝒊𝒍 ))
In functional programming, lists are defined as elements of 𝑳𝒊𝒔𝒕 𝛼. The syntax [𝛼] is used instead of 𝑳𝒊𝒔𝒕 𝛼, the constructor 𝑵𝒊𝒍 is
written as [ ], and the constructor 𝑪𝒐𝒏𝒔 is written as infix operator (∶). Moreover, (∶) associates to the right, so
1,2,3 = 1: 2: 3: [ ] = 1 ∶ 2 ∶ 3 ∶ [ ]
In other words, the special syntax on the left can be regarded as an abbreviation for the syntax on the right, which is also special,
but only by virtue of the fact that the constructors are given nonstandard names.
Like functions over other datatypes, functions over lists can be defined by pattern matching.
Richard Bird
29. Before moving on to the topic of induction over lists, Richard Bird gives
an example of a function defined over lists using pattern matching, but
the function he chooses is the equality function, whereas we are going
to choose the sum function, just to keep things simpler.
𝑠𝑢𝑚 ∷ [𝑰𝒏𝒕] → 𝑰𝒏𝒕
𝑠𝑢𝑚 [ ] = 0
𝑠𝑢𝑚 𝑥: 𝑥𝑠 = 𝑥 + (𝑠𝑢𝑚 𝑥𝑠)
val sum : List[Int] => Int = {
case Nil => 0
case x :: xs => x + sum(xs)
}
assert( sum( 1 :: (2 :: (3 :: Nil)) ) == 6)
30. sealed trait Nat
case class Succ(n: Nat) extends Nat
case object Zero extends Nat
sealed trait List[+A]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
case object Nil extends List[Nothing]
val `(+)`: Nat => Nat => Nat =
m => { case Zero => m
case Succ(n) => Succ(m + n) }
implicit class NatSyntax(m: Nat){
def +(n: Nat) = `(+)`(m)(n)
}
val sum: List[Nat] => Nat = {
case Nil => Zero
case Cons(x, xs) => x + sum(xs)
}
assert( sum( Cons(
Succ(Zero), // 1
Cons(
Succ(Succ(Zero)), // 2
Cons(
Succ(Succ(Succ(Zero))), // 3
Nil))) )
== Succ(Succ(Succ(Succ(Succ(Succ(Zero))))))) // 6
𝑠𝑢𝑚 ∷ 𝑳𝒊𝒔𝒕 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑠𝑢𝑚 𝑵𝒊𝒍 = 𝒁𝒆𝒓𝒐
𝑠𝑢𝑚 𝑪𝒐𝒏𝒔 𝑥 𝑥𝑠 = 𝑥 + (𝑠𝑢𝑚 𝑥𝑠)
+ ∷ 𝑵𝒂𝒕 → 𝑵𝒂𝒕 → 𝑵𝒂𝒕
𝑚 + 𝒁𝒆𝒓𝒐 = 𝑚
𝑚 + 𝑺𝒖𝒄𝒄 𝑛 = 𝑺𝒖𝒄𝒄 𝑚 + 𝑛
𝐝𝐚𝐭𝐚 𝑵𝒂𝒕 = 𝒁𝒆𝒓𝒐 | 𝑺𝒖𝒄𝒄 𝑵𝒂𝒕
𝐝𝐚𝐭𝐚 𝐋𝐢𝐬𝐭 α = 𝐍𝐢𝐥 | 𝐂𝐨𝐧𝐬 α (𝐋𝐢𝐬𝐭 α)
Same as on the previous slide, but this time
using Nat rather than Int, just for fun.
31. 4.1.2 Induction over Lists
Recall from section 3.2 that, for the datatype 𝑵𝒂𝒕 of natural numbers, structural induction is based on three cases: every element
of 𝑵𝒂𝒕 is either ⊥, or 𝒁𝒆𝒓𝒐, or else has the form 𝑺𝒖𝒄𝒄 𝑛 for some element 𝑛 of 𝑵𝒂𝒕. Similarly, structural induction on lists is also
based on on three cases: every list is either the undefined list ⊥, the empty list [ ], or else has the form 𝑥: 𝑥𝑠 for some 𝑥 and list
𝑥𝑠.
To show by induction that a proposition 𝑃(𝑥𝑠) holds for all lists 𝑥𝑠 it suffices therefore to establish three cases:
Case (⊥). That 𝑃(⊥) holds.
Case ([ ]). That 𝑃([ ]) holds.
Case 𝑥: 𝑥𝑠 . That if 𝑃(𝑥𝑠) holds, then 𝑃(𝑥: 𝑥𝑠) also holds for every 𝑥.
If we prove only the second two cases, then we can conclude only that 𝑃(𝑥𝑠) holds for every finite list; if we prove only the first
and third cases. Then we can conclude only that 𝑃(𝑥𝑠) holds for every partial list. If 𝑃 takes the form of an equation, as all of our
laws do, then proving the first and third cases is sufficient to show that 𝑃(𝑥𝑠) holds for every infinite list. Partial lists and infinite
lists are described in the following section. Examples of induction proofs are given throughout the remainder of the chapter.
Richard Bird
32. Richard Bird provides other examples of recursive functions
over lists. Let’s see some of them: list concatenation,
flattening of lists of lists, list reversal and length of a list.
When looking at the first one, i.e. concatenation, let’s also
see an example of proof by structural induction on lists.
@philip_schwarz
33. 4.2.1 Concatenation
Two lists can be concatenated to form one longer list. This function is denoted by the binary operator ⧺ (pronounced
‘concatenate’). As two simple examples, we have
? 1,2,3 ⧺ 4,5
1,2,3,4,5
? 1,2 ⧺ ⧺ 1
1,2,1
The formal definition of ⧺ is
(⧺) ∷ [α] → [α] → [α]
⧺ 𝑦𝑠 = 𝑦𝑠
𝑥: 𝑥𝑠 ⧺ 𝑦𝑠 = 𝑥 ∶ (𝑥𝑠 ⧺ 𝑦𝑠)
Concatenation takes two lists, both of the same type, and produces a third list, again of the same type. Hence the type assignment.
The definition of ⧺ is by pattern matching on the left-hand argument; the two patterns are disjoint and cover all cases, apart from
the undefined list ⊥. It follows by case exhaustion that ⊥ ⧺ 𝑦𝑠 = ⊥.
However, it is not the case that 𝑦𝑠 ⧺ ⊥ = ⊥. For example,
? 1,2,3 ⧺ 𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑
1,2,3{𝐼𝑛𝑡𝑒𝑟𝑟𝑢𝑝𝑡𝑒𝑑!}
The list 1,2,3 ⧺ ⊥ is a partial list; In full form it is the list 1: 2: 3: ⊥. The evaluator can compute the first three elements, but
thereafter it goes into a nonterminating computation, so we interrupt it.
The second equation for ⧺ is very succinct and requires some thought. Once one has come to grips with the definition of ⧺, one Richard Bird
34. has understood a good deal about how lists work in functional programming. Note that the number of steps required to compute
𝑥𝑠 ⧺ 𝑦𝑠 is proportional to the number of elements in 𝑥𝑠.
1, 2 ⧺ 3, 4, 5
= { notation}
(1 ∶ 2 ∶ ⧺ (3 ∶ (4 ∶ 5 ∶ [ ] ))
= { second equation for ⧺, i.e. 𝑥: 𝑥𝑠 ⧺ 𝑦𝑠 = 𝑥 ∶ (𝑥𝑠 ⧺ 𝑦𝑠) }
1 ∶ ( 2 ∶ ⧺ (3 ∶ (4 ∶ 5 ∶ [ ] )))
= { second equation for ⧺ }
1 ∶ (2 ∶ ( ⧺ (3 ∶ (4 ∶ 5 ∶ [ ] ))))
= { first equation for ⧺ i.e. , ⧺ 𝑦𝑠 = 𝑦𝑠 }
1 ∶ (2 ∶ (3 ∶ (4 ∶ 5 ∶ [ ] )))
= { notation}
1, 2, 3, 4, 5
Concatenation is an associative operation with unit :
𝑥𝑠 ⧺ 𝑦𝑠 ⧺ 𝑧𝑠 = 𝑥𝑠 ⧺ (𝑦𝑠 ⧺ 𝑧𝑠)
𝑥𝑠 ⧺ = ⧺ 𝑥𝑠 = 𝑥𝑠
Let us now prove by induction that ⧺ is associative.
Proof. The proof is by induction on 𝑥𝑠.
Case (⊥). For the left-hand side, we reason
⊥ ⧺ (𝑦𝑠 ⧺ 𝑧𝑠)
= { case exhaustion}
⊥ ⧺ 𝑧𝑠
= { case exhaustion}
⊥
Richard Bird
35. The right-hand side simplifies to ⊥ as well, establishing the case.
Case ([ ]). For the left hand side, we reason
[ ] ⧺ (𝑦𝑠 ⧺ 𝑧𝑠)
= { first equation for ⧺ i.e. , ⧺ 𝑦𝑠 = 𝑦𝑠 }
(𝑦𝑠 ⧺ 𝑧𝑠)
The right-hand side simplifies to(𝑦𝑠 ⧺ 𝑧𝑠) as well, establishing the case.
Case 𝑥 ∶ 𝑥𝑠 . For the left hand side, we reason
((x ∶ 𝑥𝑠) ⧺ 𝑦𝑠) ⧺ 𝑧𝑠
= { second equation for ⧺, i.e. 𝑥: 𝑥𝑠 ⧺ 𝑦𝑠 = 𝑥 ∶ (𝑥𝑠 ⧺ 𝑦𝑠) }
( 𝑥 ∶ 𝑥𝑠 ⧺ 𝑦𝑠) ⧺ 𝑧𝑠
= { second equation for ⧺ }
𝑥 ∶ ( 𝑥𝑠 ⧺ 𝑦𝑠 ⧺ 𝑧𝑠)
= { induction hypothesis }
𝑥 ∶ (𝑥𝑠 ⧺ 𝑦𝑠 ⧺ 𝑧𝑠)
For the right-hand side we reason
(x ∶ 𝑥𝑠) ⧺ (𝑦𝑠 ⧺ 𝑧𝑠)
= { second equation for ⧺, i.e. 𝑥 ∶ 𝑥𝑠 ⧺ 𝑦𝑠 = 𝑥 ∶ (𝑥𝑠 ⧺ 𝑦𝑠) }
𝑥 ∶ (𝑥𝑠 ⧺ (𝑦𝑠 ⧺ 𝑧𝑠))
The two sides are equal, establishing the case.
…Note that associativity is proved for all lists, finite, partial or infinite. Hence we can assert that ⧺ is associative without
qualification…. Richard Bird
36. 4.2.2 Concat
Concatenation performs much the same function for lists as the union operator ∪ does for sets. A companion function is concat,
which concatenates a list of lists into one long list. This function, which roughly corresponds to the big-union operator ⋃ for sets
of sets, is defined by
concat ∷ [ α ] → [α]
concat =
concat 𝑥𝑠: 𝑥𝑠𝑠 = 𝑥𝑠 ⧺ 𝑐𝑜𝑛𝑐𝑎𝑡 𝑥𝑠𝑠
For example,
?concat [ 1, 2 , , 3, 2,1 ]
1,2,3,2,1
4.2.3 Reverse
Another basic function on lists is reverse, the function that reverses the order of elements in a finite list. For example:
? reverse “Madam, I’m Adam.”
“.MadA m’I ,madaM”
The definition is
reverse ∷ α → [α]
reverse =
reverse 𝑥 ∶ 𝑥𝑠 = 𝑟𝑒𝑣𝑒𝑟𝑠𝑒 𝑥𝑠 ⧺ [𝑥]
In words, to reverse a list 𝑥 ∶ 𝑥𝑠 , one reverses 𝑥𝑠, and then adds 𝑥 to the end. As a program, the above definition is not very Richard Bird
37. efficient: on a list of length 𝑛, it will need a number of reduction steps proportional to 𝑛2 to deliver the reversed list. The first
element will be appended to the end of a list of length 𝑛 − 1 , which will take about 𝑛 − 1 steps, the second element will be
appended to a list of length 𝑛 − 2 , taking 𝑛 − 2 steps, and so on. The total time is therefore about
𝑛 − 1 + 𝑛 − 2 + ⋯ 1 = 𝑛(𝑛 − 1)/2 steps
A more precise analysis is given in chapter 7, and a more efficient program for reverse is given in section 4.5.
4.2.2 Length
The length of a list is the number of elements it contains:
𝑙𝑒𝑛𝑔𝑡ℎ ∷ [α] → 𝑰𝒏𝒕
𝑙𝑒𝑛𝑔𝑡ℎ [ ] = 0
𝑙𝑒𝑛𝑔𝑡ℎ 𝑥: 𝑥𝑠 = 1 + 𝑙𝑒𝑛𝑔𝑡ℎ 𝑥𝑠
The nature of the list element is irrelevant when computing the length of a list, whence the type assignment. For example,
? 𝑙𝑒𝑛𝑔𝑡ℎ [𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑, 𝑢𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑]
2
However, not every list has a well-defined length. In particular, the partial lists ⊥, 𝑥 ∶ ⊥, 𝑥 ∶ 𝑦 ∶ ⊥, and so on, have an undefined
length. Only finite lists have well-defined lengths. The list ⊥, ⊥ is a finite list, not a partial list, because it is the list ⊥ ∶ ⊥ ∶ [ ],
which ends in [ ], not ⊥. The computer cannot produce the elements, but it can produce the length of the list.
…
Richard Bird
38. 4.3 Map and filter
Two useful functions on lists are map and £ilter. The function map applies a function to each element of a list. For example
? 𝑚ap square [9, 3] ? 𝑚ap (<3) [1, 2, 3] ? 𝑚ap nextLetter “HAL”
[81, 9] [𝑇𝑟𝑢𝑒, 𝑇𝑟𝑢𝑒, 𝐹𝑎𝑙𝑠𝑒] “IBM”
The definition is
map ∷ (α → 𝛽) → [α] → [𝛽]
map f =
map f 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 ∶ map 𝑓 𝑥𝑠
…
4.3 filter
The second function, £ilter, takes a Boolean function 𝑝 and a list 𝑥𝑠 and returns that sublist of 𝑥𝑠 whose elements satisfy p. For
example,
? £ilter even [1,2,4,5,32] ? (sum Š map square Š £ilter even) [1. . 10]
[2,4,32] 220
The last example asks for the sum of the squares of the even integers in the range 1..10.
The definition of filter is
£ilter ∷ (α → 𝐵𝑜𝑜𝑙) → [α] → [α]
£ilter p =
£ilter p 𝑥 ∶ 𝑥𝑠 = 𝐢𝐟 𝑝 𝑥 𝐭𝐡𝐞𝐧 𝑥 ∶ £ilter p 𝑥𝑠 𝐞𝐥𝐬𝐞 £ilter p 𝑥𝑠
… Richard Bird
40. 4.5 The fold functions
We have seen in the case of the datatype 𝑵𝒂𝒕 that many recursive definitions can be expressed very succinctly using a
suitable 𝑓𝑜𝑙𝑑 operator. Exactly the same is true of lists. Consider the following definition of a function ℎ :
ℎ [ ] = 𝑒
ℎ 𝑥: 𝑥𝑠 = 𝑥 ⊕ ℎ 𝑥𝑠
The function ℎ works by taking a list, replacing [ ] by 𝑒 and ∶ by ⊕, and evaluating the result. For example, ℎ converts the list
𝑥1 ∶ (𝑥2 ∶ 𝑥3 ∶ 𝑥4 ∶ )
to the value
𝑥1 ⊕ (𝑥2 ⊕ (𝑥3 ⊕ 𝑥4 ⊕ 𝑒 ))
Since ∶ associates to the right, there is no need to put in parentheses in the first expression. However, we do need to put in
parentheses in the second expression because we do not assume that ⊕ associates to the right.
The pattern of definition given by ℎ is captured in a function 𝑓𝑜𝑙𝑑𝑟 (prounced ‘fold right’) defined as follows:
𝑓𝑜𝑙𝑑𝑟 ∷ 𝛼 → 𝛽 → 𝛽 → 𝛽 → 𝛼 → 𝛽
𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 = 𝑒
𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 𝑥: 𝑥𝑠 = 𝑓 𝑥 𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 𝑥𝑠
We can now write h = 𝑓𝑜𝑙𝑑𝑟 ⊕ 𝑒. The first argument of 𝑓𝑜𝑙𝑑𝑟 is a binary operator that takes an 𝛼-value on its left and an a 𝛽–
value on its right, and delivers a 𝛽–value. The second argument of 𝑓𝑜𝑙𝑑𝑟 is a 𝛽-value. The third argument is of type 𝛼 , and the
result is of type 𝛽. In many cases, 𝛼 and 𝛽 will be instantiated to the same type, for instance when ⊕ denotes an associative
operation. Richard Bird
41. In the next slide we look at how some of the recursively
defined functions on lists that we have recently seen can be
redefined in terms of 𝑓𝑜𝑙𝑑𝑟.
To aid comprehension, I have added the original function
definitions next to the new definitions in terms of 𝑓𝑜𝑙𝑑𝑟. For
reference, I also added the definition of 𝑓𝑜𝑙𝑑𝑟.
43. On the next slide, the same code translated into Scala
@philip_schwarz
44. def foldr[A,B](f: A => B => B)(e: B)(xs: List[A]): B =
xs match {
case Nil => e
case x::xs => f(x)(foldr(f)(e)(xs))
}
def concatenate[A]: List[A] => List[A] => List[A] =
xs => ys => xs match {
case Nil => ys
case x :: xs => x :: concatenate(xs)(ys)
}
def concat[A]: List[List[A]] => List[A] =
foldr(concatenate[A])(Nil)
def reverse[A]: List[A] => List[A] = {
def snoc[A]: A => List[A] => List[A] =
x => xs => concatenate(xs)(List(x))
foldr(snoc[A])(Nil)
}
def length[A]: List[A] => Int = {
def oneplus[A]: A => Int => Int = x => n => 1 + n
foldr(oneplus)(0)
}
val sum: List[Int] => Int = {
val plus: Int => Int => Int = a => b => a + b
foldr(plus)(0)
}
def map[A,B]: (A => B) => List[A] => List[B] = {
def cons: B => List[B] => List[B] = x => xs => x :: xs
f => foldr(cons compose f)(Nil)
}
𝑓𝑜𝑙𝑑𝑟 ∷ 𝛼 → 𝛽 → 𝛽 → 𝛽 → 𝛼 → 𝛽
𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 = 𝑒
𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 𝑥: 𝑥𝑠 = 𝑓 𝑥 𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 𝑥𝑠
(⧺) ∷ [α] → [α] → [α]
⧺ 𝑦𝑠 = 𝑦𝑠
𝑥: 𝑥𝑠 ⧺ 𝑦𝑠 = 𝑥 ∶ (𝑥𝑠 ⧺ 𝑦𝑠)
concat ∷ [ α ] → [α]
concat = 𝑓𝑜𝑙𝑑𝑟 (⧺) [ ]
reverse ∷ α → [α]
reverse = 𝑓𝑜𝑙𝑑𝑟 𝑠𝑛𝑜𝑐
𝒘𝒉𝒆𝒓𝒆 𝑠𝑛𝑜𝑐 𝑥 𝑥𝑠 = 𝑥𝑠 ⧺ [𝑥]
𝑙𝑒𝑛𝑔𝑡ℎ ∷ [α] → 𝑰𝒏𝒕
𝑙𝑒𝑛𝑔𝑡ℎ = 𝑓𝑜𝑙𝑑𝑟 𝑜𝑛𝑒𝑝𝑙𝑢𝑠 0
𝒘𝒉𝒆𝒓𝒆 𝑜𝑛𝑒𝑝𝑙𝑢𝑠 𝑥 𝑛 = 1 + 𝑛
𝑠𝑢𝑚 ∷ [𝑰𝒏𝒕] → 𝑰𝒏𝒕
𝑠𝑢𝑚 = 𝑓𝑜𝑙𝑑𝑟 + 0
map ∷ (α → 𝛽) → [α] → [𝛽]
map 𝑓 = 𝑓𝑜𝑙𝑑𝑟 𝑐𝑜𝑛𝑠 T 𝑓
𝒘𝒉𝒆𝒓𝒆 𝑐𝑜𝑛𝑠 𝑥 𝑥𝑠 = 𝑥 ∶ 𝑥𝑠
45. assert( concatenate(List(1,2,3))(List(4,5)) == List(1,2,3,4,5) )
assert( concat(List(List(1,2), List(3), List(4,5))) == List(1,2,3,4,5) )
assert( reverse(List(1,2,3,4,5)) == List(5,4,3,2,1) )
assert( length(List(0,1,2,3,4,5)) == 6 )
assert( sum(List(2,3,4)) == 9 )
val mult: Int => Int => Int = a => b => a * b
assert( map(mult(10))(List(1,2,3)) == List(10,20,30))
Here a some sample tests for the
Scala functions on the previous slide.
46. It turns out that if it is possible to define a function on lists both using a recursive definition and
using a definition in terms of 𝑓𝑜𝑙𝑑𝑟, then there is a technique that can be used to go from the
recursive definition to the definition using 𝑓𝑜𝑙𝑑𝑟.
I came across the technique in the following paper by the author of Programming in Haskell:
The tutorial (which I shall be referring to as TUEF), shows how to apply the technique to the
sum function and the map function, which is the subject of the next five slides. Note: in the
paper, the 𝑓𝑜𝑙𝑑𝑟 function is referred to as 𝑓𝑜𝑙𝑑.
@philip_schwarz
47. 3 The universal property of fold
As with the fold operator itself, the universal property of 𝒇𝒐𝒍𝒅 also has its origins in recursion theory. The first systematic use of the
universal property in functional programming was by Malcolm (1990a), in his generalisation of Bird and Meerten’s theory of lists (Bird,
1989; Meertens, 1983) to arbitrary regular datatypes. For finite lists, the universal property of 𝒇𝒐𝒍𝒅 can be stated as the following
equivalence between two definitions for a function 𝑔 that processes lists:
𝑔 = 𝑣 ⟺ 𝑔 = 𝑓𝑜𝑙𝑑 𝑓 𝑣
𝑔 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 𝑔 𝑥𝑠
In the right-to-left direction, substituting 𝑔 = 𝑓𝑜𝑙𝑑 𝑓 𝑣 into the two equations for 𝑔 gives the recursive definition for 𝑓𝑜𝑙𝑑. Conversely,
in the left-to-right direction the two equations for g are precisely the assumptions required to show that 𝑔 = 𝑓𝑜𝑙𝑑 𝑓 𝑣 using a simple
proof by induction on finite lists (Bird, 1998). Taken as a whole, the universal property states that for finite lists the function 𝑓𝑜𝑙𝑑 𝑓 𝑣 is
not just a solution to its defining equations, but in fact the unique solution…. The universal property of 𝒇𝒐𝒍𝒅 can be generalised to
handle partial and infinite lists (Bird, 1998), but for simplicity we only consider finite lists in this article.
Graham Hutton
@haskellhutt
48. 3.3 Universality as a definition principle
As well as being used as a proof principle, the universal property of 𝒇𝒐𝒍𝒅 can also be used as a definition principle that guides the
transformation of recursive functions into definitions using 𝑓𝑜𝑙𝑑. As a simple first example, consider the recursively defined function
𝑠𝑢𝑚 that calculates the sum of a list of numbers:
𝑠𝑢𝑚 ∷ 𝐼𝑛𝑡 → 𝐼𝑛𝑡
𝑠𝑢𝑚 = 0
𝑠𝑢𝑚 𝑥 ∶ 𝑥𝑠 = 𝑥 + 𝑠𝑢𝑚 𝑥𝑠
Suppose now that we want to redefine 𝑠𝑢𝑚 using 𝑓𝑜𝑙𝑑. That is, we want to solve the equation 𝑠𝑢𝑚 = 𝑓𝑜𝑙𝑑 𝑓 𝑣 for a function f and a
value 𝑣. We begin by observing that the equation matches the right-hand side of the universal property, from which we conclude that
the equation is equivalent to the following two equations:
𝑠𝑢𝑚 = 𝑣
𝑠𝑢𝑚 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 (𝑠𝑢𝑚 𝑥𝑠)
From the first equation and the definition of 𝑠𝑢𝑚, it is immediate that 𝑣 = 0.
𝑔 = 𝑣 ⟺ 𝑔 = 𝑓𝑜𝑙𝑑 𝑓 𝑣
𝑔 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 𝑔 𝑥𝑠
Graham Hutton
@haskellhutt
universal property of 𝒇𝒐𝒍𝒅
49. From the second equation, we calculate a definition for 𝑓 as follows:
𝑠𝑢𝑚 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 (𝑠𝑢𝑚 𝑥𝑠)
⇔ { Definition of 𝑠𝑢𝑚 }
𝑥 + 𝑠𝑢𝑚 𝑥𝑠 = 𝑓 𝑥 (𝑠𝑢𝑚 𝑥𝑠)
⇐ { † Generalising (𝑠𝑢𝑚 𝑥𝑠) to 𝑦 }
𝑥 + 𝑦 = 𝑓 𝑥 𝑦
⇔ { Functions }
𝑓 = (+)
That is, using the universal property we have calculated that:
𝑠𝑢𝑚 = 𝑓𝑜𝑙𝑑 + 0
Note that the key step (†) above in calculating a definition for 𝑓 is the generalisation of the expression 𝑠𝑢𝑚 𝑥𝑠 to a fresh variable 𝑦. In fact,
such a generalisation step is not specific to the 𝑠𝑢𝑚 function, but will be a key step in the transformation of any recursive function into a
definition using 𝑓𝑜𝑙𝑑 in this manner.
𝑠𝑢𝑚 ∷ 𝐼𝑛𝑡 → 𝐼𝑛𝑡
𝑠𝑢𝑚 = 0
𝑠𝑢𝑚 𝑥 ∶ 𝑥𝑠 = 𝑥 + 𝑠𝑢𝑚 𝑥𝑠
𝑠𝑢𝑚 = 𝑣
𝑠𝑢𝑚 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 (𝑠𝑢𝑚 𝑥𝑠)
Graham Hutton
@haskellhutt
50. Of course, the 𝑠𝑢𝑚 example above is rather artificial, because the definition of 𝑠𝑢𝑚 using 𝑓𝑜𝑙𝑑 is immediate. However, there are many
examples of functions whose definition using 𝑓𝑜𝑙𝑑 is not so immediate. For example, consider the recursively defined function 𝑚𝑎𝑝 𝑓 that
applies a function 𝑓 to each element of a list:
𝑚𝑎𝑝 ∷ 𝛼 → 𝛽 → 𝛼 → 𝛽
𝑚𝑎𝑝 𝑓 =
𝑚𝑎𝑝 𝑓 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 ∶ 𝑚𝑎𝑝 𝑓 𝑥𝑠
To redefine 𝑚𝑎𝑝 𝑓 using 𝑓𝑜𝑙𝑑 we must solve the equation 𝑚𝑎𝑝 𝑓 = 𝑓𝑜𝑙𝑑 𝑣 𝑔 for a function 𝑔 and a value 𝑣. By appealing to the
universal property, we conclude that this equation is equivalent to the following two equations:
𝑚𝑎𝑝 𝑓 = 𝑣
𝑚𝑎𝑝 𝑓 𝑥 ∶ 𝑥𝑠 = 𝑔 𝑥 (𝑚𝑎𝑝 𝑓 𝑥𝑠)
From the first equation and the definition of 𝑚𝑎𝑝 it is immediate that 𝑣 = [ ].
Graham Hutton
@haskellhutt
𝑔 = 𝑣 ⟺ 𝑔 = 𝑓𝑜𝑙𝑑 𝑓 𝑣
𝑔 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 𝑔 𝑥𝑠
substitute 𝑚𝑎𝑝 𝑓 for 𝑔 and 𝑔 for 𝑓
universal property of 𝒇𝒐𝒍𝒅
51. From the second equation, we calculate a definition for 𝑔 as follows:
𝑚𝑎𝑝 𝑓 𝑥 ∶ 𝑥𝑠 = 𝑔 𝑥 (𝑚𝑎𝑝 𝑓 𝑥𝑠)
⇔ { Definition of 𝑚𝑎𝑝 }
𝑓 𝑥 ∶ 𝑚𝑎𝑝 𝑓 𝑥𝑠 = 𝑔 𝑥 (𝑚𝑎𝑝 𝑓 𝑥𝑠)
⟸ { Generalising (𝑚𝑎𝑝 𝑓 𝑥𝑠) to 𝑦𝑠 }
𝑓 𝑥 ∶ 𝑦𝑠 = 𝑔 𝑥 𝑦𝑠
⇔ { Functions }
𝑔 = 𝜆𝑥 𝑦𝑠 → 𝑓 𝑥 ∶ 𝑦𝑠
That is, using the universal property we have calculated that
𝑚𝑎𝑝 𝑓 = 𝑓𝑜𝑙𝑑 𝜆𝑥 𝑦𝑠 → 𝑓 𝑥 ∶ 𝑦𝑠 [ ]
In general, any function on lists that can be expressed using the 𝑓𝑜𝑙𝑑 operator can be transformed into such a definition using the
universal property of 𝑓𝑜𝑙𝑑.
Graham Hutton
@haskellhutt
𝑚𝑎𝑝 ∷ 𝛼 → 𝛽 → 𝛼 → 𝛽
𝑚𝑎𝑝 𝑓 = 𝑣
𝑚𝑎𝑝 𝑓 𝑥 ∶ 𝑥𝑠 = 𝑔 𝑥 (𝑚𝑎𝑝 𝑓 𝑥𝑠)
52. There are several other interesting things in TUEF that we’ll be looking at.
I like its description of 𝑓𝑜𝑙𝑑𝑟 (see right), because it reiterates a key point (see left) made by Richard Bird about recursive functions on lists.
Consider the following definition of a function ℎ :
ℎ [ ] = 𝑒
ℎ 𝑥: 𝑥𝑠 = 𝑥 ⊕ ℎ 𝑥𝑠
The function ℎ works by taking a list, replacing [ ] by 𝑒 and ∶ by ⊕, and evaluating
the result. For example, ℎ converts the list
𝑥1 ∶ (𝑥2 ∶ 𝑥3 ∶ 𝑥4 ∶ )
to the value
𝑥1 ⊕ (𝑥2 ⊕ (𝑥3 ⊕ 𝑥4 ⊕ 𝑒 ))
Since ∶ associates to the right, there is no need to put in parentheses in the first
expression. However, we do need to put in parentheses in the second expression
because we do not assume that ⊕ associates to the right.
The pattern of definition given by ℎ is captured in a function 𝑓𝑜𝑙𝑑𝑟 (pronounced ‘fold
right’) defined as follows:
𝑓𝑜𝑙𝑑𝑟 ∷ 𝛼 → 𝛽 → 𝛽 → 𝛽 → 𝛼 → 𝛽
𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 = 𝑒
𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 𝑥: 𝑥𝑠 = 𝑓 𝑥 𝑓𝑜𝑙𝑑𝑟 𝑓 𝑒 𝑥𝑠
2 The fold operator
The fold operator has its origins in recursion theory (Kleene, 1952), while the use of fold as
a central concept in a programming language dates back to the reduction operator of APL
(Iverson, 1962), and later to the insertion operator of FP (Backus, 1978). In Haskell, the fold
operator for lists can be defined as follows:
𝑓𝑜𝑙𝑑 :: 𝛼 → 𝛽 → 𝛽 → 𝛽 → 𝛼 → 𝛽
𝑓𝑜𝑙𝑑 𝑓 𝑣 = 𝑣
𝑓𝑜𝑙𝑑 𝑓 𝑣 𝑥 ∶ 𝑥𝑠 = 𝑓 𝑥 𝑓𝑜𝑙𝑑 𝑓 𝑣 𝑥𝑠
That is, given a function f of type 𝛼 → 𝛽 → 𝛽 and a value 𝑣 of type 𝛽, the function
𝑓𝑜𝑙𝑑 𝑓 𝑣 processes a list of type 𝛼 to give a value of type 𝛽 by replacing the nil
constructor at the end of the list by the value 𝑣, and each cons constructor ∶ within
the list by the function 𝑓. In this manner, the 𝑓𝑜𝑙𝑑 operator encapsulates a simple pattern
of recursion for processing lists, in which the two constructors for lists are simply
replaced by other values and functions.
53. (⧺) ∷ [α] → [α] → [α]
⧺ 𝑦𝑠 = 𝑦𝑠
𝑥: 𝑥𝑠 ⧺ 𝑦𝑠 = 𝑥 ∶ (𝑥𝑠 ⧺ 𝑦𝑠)
Concatenation takes two lists, both of
the same type, and produces a third
list, again of the same type.
Remember the list concatenation function we saw earlier?
In TUEF we find a definition of concatenation in terms of 𝑓𝑜𝑙𝑑𝑟 (which it calls 𝑓𝑜𝑙𝑑)
(⧺) ∷ [α] → [α] → [α]
⧺ 𝑦𝑠 = 𝑓𝑜𝑙𝑑 ∶ 𝑦𝑠
assert( concatenate(List(1,2,3))(List(4,5)) == List(1,2,3,4,5) )
def concatenate[A]: List[A] => List[A] => List[A] =
xs => ys => xs match {
case Nil => ys
case x :: xs => x :: concatenate(xs)(ys)
}
def concatenate[A]: List[A] => List[A] => List[A] = {
def cons: A => List[A] => List[A] =
x => xs => x :: xs
xs => ys => foldr(cons)(ys)(xs)
}
54. Remember the £ilter function we saw earlier?
In TUEF we find a definition of £ilter in terms of 𝑓𝑜𝑙𝑑𝑟 (which as we saw, it calls 𝑓𝑜𝑙𝑑)
£ilter ∷ (α → 𝐵𝑜𝑜𝑙) → [α] → [α]
£ilter p =
£ilter p 𝑥 ∶ 𝑥𝑠 = 𝐢𝐟 𝑝 𝑥 𝐭𝐡𝐞𝐧 𝑥 ∶ £ilter p 𝑥𝑠 𝐞𝐥𝐬𝐞 £ilter p 𝑥𝑠
£ilter ∷ (α → 𝐵𝑜𝑜𝑙) → [α] → [α]
£ilter p = 𝑓𝑜𝑙𝑑 (𝜆𝑥 𝑥𝑠 → 𝐢𝐟 𝑝 𝑥 𝐭𝐡𝐞𝐧 𝑥 ∶ 𝑥𝑠 𝐞𝐥𝐬𝐞 𝑥𝑠) [ ]
def filter[A]: (A => Boolean) => List[A] => List[A] = p => {
case Nil => Nil
case x :: xs => if (p(x)) x :: filter(p)(xs) else filter(p)(xs)
}
def filter[A]: (A => Boolean) => List[A] => List[A] = p =>
foldr((x:A) => (xs:List[A]) => if (p(x)) (x::xs) else xs)(Nil)
val gt: Int => Int => Boolean = x => y => y > x
assert(filter(gt(5))(List(10,2,8,5,3,6)) == List(10,8,6))
55. Not every function on lists can be defined as an instance of 𝑓𝑜𝑙𝑑𝑟. For example, zip cannot be so defined. Even for those that
can, an alternative definition may be more efficient. To illustrate, suppose we want a function decimal that takes a list of digits
and returns the corresponding decimal number; thus
𝑑𝑒𝑐𝑖𝑚𝑎𝑙 [𝑥0, 𝑥1, … , 𝑥n] = ∑%&'
(
𝑥 𝑘10((*%)
It is assumed that the most significant digit comes first in the list. One way to compute decimal efficiently is by a process of
multiplying each digit by ten and adding in the following digit. For example
𝑑𝑒𝑐𝑖𝑚𝑎𝑙 𝑥0, 𝑥1, 𝑥2 = 10 × 10 × 10 × 0 + 𝑥0 + 𝑥1 + 𝑥2
This decomposition of a sum of powers is known as Horner’s rule.
Suppose we define ⊕ by 𝑛 ⊕ 𝑥 = 10 × 𝑛 + 𝑥. Then we can rephrase the above equation as
𝑑𝑒𝑐𝑖𝑚𝑎𝑙 𝑥0, 𝑥1, 𝑥2 = (0 ⊕ 𝑥0) ⊕ 𝑥1 ⊕ 𝑥2
This is almost like an instance of 𝑓𝑜𝑙𝑑𝑟, except that the grouping is the other way round, and the starting value appears on the
left, not on the right. In fact the computation is dual: instead of processing from right to left, the computation processes from
left to right.
This example motivates the introduction of a second fold operator called 𝑓𝑜𝑙𝑑𝑙 (pronounced ‘fold left’). Informally:
𝑓𝑜𝑙𝑑𝑙 ⊕ 𝑒 𝑥0, 𝑥1, … , 𝑥 𝑛 − 1 = … ((𝑒 ⊕ 𝑥0) ⊕ 𝑥1) … ⊕ 𝑥 𝑛 − 1
The parentheses group from the left, which is the reason for the name. The full definition of 𝑓𝑜𝑙𝑑𝑙 is
𝑓𝑜𝑙𝑑𝑙 ∷ 𝛽 → 𝛼 → 𝛽 → 𝛽 → 𝛼 → 𝛽
𝑓𝑜𝑙𝑑𝑙 𝑓 𝑒 = 𝑒
𝑓𝑜𝑙𝑑𝑙 𝑓 𝑒 𝑥: 𝑥𝑠 = 𝑓𝑜𝑙𝑑𝑙 𝑓 𝑓 𝑒 𝑥 𝑥𝑠 Richard Bird
56. For example
𝑓𝑜𝑙𝑑𝑙 ⊕ 𝑒 𝑥0, 𝑥1, 𝑥2
= 𝑓𝑜𝑙𝑑𝑙 ⊕ 𝑒 ⊕ 𝑥0 𝑥1, 𝑥2
= 𝑓𝑜𝑙𝑑𝑙 ⊕ 𝑒 ⊕ 𝑥0 ⊕ 𝑥1 𝑥2
= 𝑓𝑜𝑙𝑑𝑙 ⊕ (( 𝑒 ⊕ 𝑥0 ⊕ 𝑥1) ⊕ 𝑥2) [ ]
= ((𝑒 ⊕ 𝑥0) ⊕ 𝑥1) ⊕ 𝑥2
If ⊕ is associative with unit 𝑒, then 𝑓𝑜𝑙𝑑𝑟 ⊕ 𝑒 and 𝑓𝑜𝑙𝑑𝑙 ⊕ 𝑒 define the same function on finite lists, as we will see in
the following section.
As another example of the use of 𝑓𝑜𝑙𝑑𝑙, consider the following definition:
reverse′ ∷ α → [α]
reverse′ = 𝑓𝑜𝑙𝑑𝑙 𝑐𝑜𝑛𝑠
𝒘𝒉𝒆𝒓𝒆 𝑐𝑜𝑛𝑠 𝑥𝑠 𝑥 = 𝑥 : 𝑥𝑠
Note the order of the arguments to cons; we have 𝑐𝑜𝑛𝑠 = 𝑓𝑙𝑖𝑝 (∶), where the standard function 𝑓𝑙𝑖𝑝 is defined by 𝑓𝑙𝑖𝑝𝑓 𝑥 𝑦 =
𝑓 𝑦 𝑥. The function reverse′ , reverses a finite list. For example:
reverse′ 𝑥0, 𝑥1, 𝑥2
= 𝑐𝑜𝑛𝑠(𝑐𝑜𝑛𝑠(𝑐𝑜𝑛𝑠 [ ] 𝑥0) 𝑥1) 𝑥2
= 𝑐𝑜𝑛𝑠(𝑐𝑜𝑛𝑠 𝑥1 𝑥0) 𝑥2
= 𝑐𝑜𝑛𝑠 𝑥1, 𝑥0 𝑥2
= 𝑥2, 𝑥1, 𝑥0
One can prove that reverse′ = reverse by induction, or as an instance of a more general result in the following section. Of
greater importance than the mere fact that reverse can be defined in a different way, is that reverse′ gives a much more
efficient program: reverse′ takes time proportional to 𝑛 on a list of length 𝑛, while reverse takes time proportional to 𝑛2.
reverse ∷ α → [α]
reverse = 𝑓𝑜𝑙𝑑𝑟 𝑠𝑛𝑜𝑐
𝒘𝒉𝒆𝒓𝒆 𝑠𝑛𝑜𝑐 𝑥 𝑥𝑠 = 𝑥𝑠 ⧺ [𝑥]
Richard Bird
57. def reverse'[A]: List[A] => List[A] = {
def cons: List[A] => A => List[A] =
xs => x => x :: xs
foldl(cons)(Nil)
}
assert( reverse'(List(1,2,3,4,5)) == List(5,4,3,2,1) )
def reverse[A]: List[A] => List[A] = {
def snoc[A]: A => List[A] => List[A] =
x => xs => concatenate(xs)(List(x))
foldr(snoc[A])(Nil)
}
def concatenate[A]: List[A] => List[A] => List[A] = {
def cons: A => List[A] => List[A] =
x => xs => x :: xs
xs => ys => foldr(cons)(ys)(xs)
}
assert( reverse(List(1,2,3,4,5)) == List(5,4,3,2,1) )
(⧺) ∷ [α] → [α] → [α]
⧺ 𝑦𝑠 = 𝑓𝑜𝑙𝑑 ∶ 𝑦𝑠
reverse′ ∷ α → [α]
reverse′ = 𝑓𝑜𝑙𝑑𝑙 𝑐𝑜𝑛𝑠
𝒘𝒉𝒆𝒓𝒆 𝑐𝑜𝑛𝑠 𝑥𝑠 𝑥 = 𝑥 : 𝑥𝑠
reverse ∷ α → [α]
reverse = 𝑓𝑜𝑙𝑑𝑟 𝑠𝑛𝑜𝑐
𝒘𝒉𝒆𝒓𝒆 𝑠𝑛𝑜𝑐 𝑥 𝑥𝑠 = 𝑥𝑠 ⧺ [𝑥]
Here we can see the Scala version of
reverse’, and how it compares with reverse
@philip_schwarz
58. That’s it for part 1. I hope you enjoyed that.
There is still a lot to cover of course, so I’ll see you in part 2.