From 5c16053f84471f7ac19edd31d0f250b7857306da Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Mon, 19 Mar 2018 20:13:02 -0400 Subject: [PATCH] Add exercises! (#4) * Add exercises! * Update contents.xcplayground --- .../Contents.swift | 1 + .../Contents.swift | 26 ++++++++++ .../contents.xcplayground | 7 ++- .../Contents.swift | 1 + .../Contents.swift | 32 ++++++++++++ .../contents.xcplayground | 7 ++- .../Contents.swift | 8 ++- .../Contents.swift | 32 ++++++++++++ .../contents.xcplayground | 7 ++- .../Contents.swift | 3 +- .../Contents.swift | 30 +++++++++++ .../contents.xcplayground | 7 ++- .../Contents.swift | 13 +---- .../Contents.swift | 51 +++++++++++++++++++ .../contents.xcplayground | 9 ++-- 15 files changed, 204 insertions(+), 30 deletions(-) rename 0004-algebraic-data-types/Algebraic Data Types.playground/{ => Pages/01-Episode.xcplaygroundpage}/Contents.swift (98%) create mode 100644 0004-algebraic-data-types/Algebraic Data Types.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift rename 0005-higher-order-functions/Higher-Order Functions.playground/{ => Pages/01-Episode.xcplaygroundpage}/Contents.swift (97%) create mode 100644 0005-higher-order-functions/Higher-Order Functions.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift rename 0006-functional-setters/Functional Setters.playground/{ => Pages/01-Episode.xcplaygroundpage}/Contents.swift (94%) create mode 100644 0006-functional-setters/Functional Setters.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift rename 0007-setters-and-key-paths/Setters and Key Paths.playground/{ => Pages/01-Episode.xcplaygroundpage}/Contents.swift (98%) create mode 100644 0007-setters-and-key-paths/Setters and Key Paths.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift rename 0008-getters-and-key-paths/Getters and Key Paths.playground/{ => Pages/01-Episode.xcplaygroundpage}/Contents.swift (94%) create mode 100644 0008-getters-and-key-paths/Getters and Key Paths.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift diff --git a/0004-algebraic-data-types/Algebraic Data Types.playground/Contents.swift b/0004-algebraic-data-types/Algebraic Data Types.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift similarity index 98% rename from 0004-algebraic-data-types/Algebraic Data Types.playground/Contents.swift rename to 0004-algebraic-data-types/Algebraic Data Types.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift index 61e17b31..aa2ca62b 100644 --- a/0004-algebraic-data-types/Algebraic Data Types.playground/Contents.swift +++ b/0004-algebraic-data-types/Algebraic Data Types.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift @@ -198,3 +198,4 @@ import Foundation //Result<(Data, URLResponse), Error> //Result //Result? +//: [See the next page](@next) for exercises! diff --git a/0004-algebraic-data-types/Algebraic Data Types.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift b/0004-algebraic-data-types/Algebraic Data Types.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..cf2b352c --- /dev/null +++ b/0004-algebraic-data-types/Algebraic Data Types.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift @@ -0,0 +1,26 @@ +/*: + # Algebraic Data Types Exercises + + 1. What algebraic operation does the function type `(A) -> B` correspond to? Try explicitly enumerating all the values of some small cases like `(Bool) -> Bool`, `(Unit) -> Bool`, `(Bool) -> Three` and `(Three) -> Bool` to get some intuition. + */ +// TODO +/*: + 2. Consider the following recursively defined data structure. Translate this type into an algebraic equation relating `List` to `A`. + */ +indirect enum List { + case empty + case cons(A, List) +} +// TODO +/*: + 3. Is `Optional>` equivalent to `Either, Optional>`? If not, what additional values does one type have that the other doesn’t? + */ +// TODO +/*: + 4. Is `Either, B>` equivalent to `Optional>`? + */ +// TODO +/*: + 5. Can you overload the `*` and `+` infix operators with functions that take any type and build up an algebraic representation using `Pair` and `Either`? + */ +// TODO diff --git a/0004-algebraic-data-types/Algebraic Data Types.playground/contents.xcplayground b/0004-algebraic-data-types/Algebraic Data Types.playground/contents.xcplayground index 63b6dd8d..d9700c2a 100644 --- a/0004-algebraic-data-types/Algebraic Data Types.playground/contents.xcplayground +++ b/0004-algebraic-data-types/Algebraic Data Types.playground/contents.xcplayground @@ -1,4 +1,7 @@ - - + + + + + \ No newline at end of file diff --git a/0005-higher-order-functions/Higher-Order Functions.playground/Contents.swift b/0005-higher-order-functions/Higher-Order Functions.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift similarity index 97% rename from 0005-higher-order-functions/Higher-Order Functions.playground/Contents.swift rename to 0005-higher-order-functions/Higher-Order Functions.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift index 8a7fdc6d..cb2f2c9c 100644 --- a/0005-higher-order-functions/Higher-Order Functions.playground/Contents.swift +++ b/0005-higher-order-functions/Higher-Order Functions.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift @@ -82,3 +82,4 @@ func filter(_ p: @escaping (A) -> Bool) -> ([A]) -> [A] { Array(1...10) |> filter { $0 > 5 } >>> map(incr >>> square) +//: [See the next page](@next) for exercises! diff --git a/0005-higher-order-functions/Higher-Order Functions.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift b/0005-higher-order-functions/Higher-Order Functions.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..6ce07661 --- /dev/null +++ b/0005-higher-order-functions/Higher-Order Functions.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift @@ -0,0 +1,32 @@ +/*: + # Higher-Order Functions Exercises + + 1. Write `curry` for functions that take 3 arguments. + */ +// TODO +/*: + 2. Explore functions and methods in the Swift standard library, Foundation, and other third party code, and convert them to free functions that compose using `curry`, `zurry`, `flip`, or by hand. + */ +// TODO +/*: + 3. Explore the associativity of function arrow `->`. Is it fully associative, _i.e._ is `((A) -> B) -> C` equivalent to `(A) -> ((B) -> C)`, or does it associate to only one side? Where does it parenthesize as you build deeper, curried functions? + */ +// TODO +/*: + 4. Write a function, `uncurry`, that takes a curried function and returns a function that takes two arguments. When might it be useful to un-curry a function? + */ +// TODO +/*: + 5. Write `reduce` as a curried, free function. What is the configuration _vs._ the data? + */ +// TODO +/*: + 6. In programming languages that lack sum/enum types one is tempted to approximate them with pairs of optionals. Do this by defining a type `struct PseudoEither` of a pair of optionals, and prevent the creation of invalid values by providing initializers. + + This is “type safe” in the sense that you are not allowed to construct invalid values, but not “type safe” in the sense that the compiler is proving it to you. You must prove it to yourself. + */ +// TODO +/*: + 7. Explore how the free `map` function composes with itself in order to transform a nested array. More specifically, if you have a doubly nested array `[[A]]`, then `map` could mean either the transformation on the inner array or the outer array. Can you make sense of doing `map >>> map`? + */ +// TODO diff --git a/0005-higher-order-functions/Higher-Order Functions.playground/contents.xcplayground b/0005-higher-order-functions/Higher-Order Functions.playground/contents.xcplayground index 63b6dd8d..5ae6818d 100644 --- a/0005-higher-order-functions/Higher-Order Functions.playground/contents.xcplayground +++ b/0005-higher-order-functions/Higher-Order Functions.playground/contents.xcplayground @@ -1,4 +1,7 @@ - - + + + + + \ No newline at end of file diff --git a/0006-functional-setters/Functional Setters.playground/Contents.swift b/0006-functional-setters/Functional Setters.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift similarity index 94% rename from 0006-functional-setters/Functional Setters.playground/Contents.swift rename to 0006-functional-setters/Functional Setters.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift index b7eb87f2..df1f7981 100644 --- a/0006-functional-setters/Functional Setters.playground/Contents.swift +++ b/0006-functional-setters/Functional Setters.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift @@ -95,16 +95,14 @@ public func map(_ f: @escaping (A) -> B) -> ([A]) -> [B] { |> first(incr) dump( -[(42, ["Swift", "Objective-C"]), (1729, ["Haskell", "Purescript"])] +[(42, ["Swift", "Objective-C"]), (1729, ["Haskell", "PureScript"])] |> (map <<< second <<< map) { $0 + "!" } ) // -let data = [(42, ["Swift", "Objective-C"]), (1729, ["Haskell", "Purescript"])] +let data = [(42, ["Swift", "Objective-C"]), (1729, ["Haskell", "PureScript"])] data .map { ($0.0, $0.1.map { $0 + "!" }) } - - - +//: [See the next page](@next) for exercises! diff --git a/0006-functional-setters/Functional Setters.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift b/0006-functional-setters/Functional Setters.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..8bcb4e5b --- /dev/null +++ b/0006-functional-setters/Functional Setters.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift @@ -0,0 +1,32 @@ +/*: + # Functional Setters Exercises + + 1. As we saw with free `map` on `Array`, define free `map` on `Optional` and use it to compose setters that traverse into an optional field. + */ +// TODO +/*: + 2. Take the following `User` struct and write a setter for its `name` property. Add another property, and add a setter for it. What are some potential issues with building these setters? + */ +struct User { + let name: String +} +// TODO +/*: + 3. Add a `location` property to `User`, which holds a `Location`, defined below. Write a setter for `userLocationName`. Now write setters for `userLocation` and `locationName`. How do these setters compose? + */ +struct Location { + let name: String +} +// TODO +/*: + 4. Do `first` and `second` work with tuples of three or more values? Can we write `first`, `second`, `third`, and `nth` for tuples of _n_ values? + */ +// TODO +/*: + 5. Write a setter for a dictionary that traverses into a key to set a value. + */ +// TODO +/*: + 6. What is the difference between a function of the form `((A) -> B) -> (C) -> (D)` and one of the form `(A) -> (B) -> (C) -> D`? + */ +// TODO diff --git a/0006-functional-setters/Functional Setters.playground/contents.xcplayground b/0006-functional-setters/Functional Setters.playground/contents.xcplayground index 63b6dd8d..5ae6818d 100644 --- a/0006-functional-setters/Functional Setters.playground/contents.xcplayground +++ b/0006-functional-setters/Functional Setters.playground/contents.xcplayground @@ -1,4 +1,7 @@ - - + + + + + \ No newline at end of file diff --git a/0007-setters-and-key-paths/Setters and Key Paths.playground/Contents.swift b/0007-setters-and-key-paths/Setters and Key Paths.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift similarity index 98% rename from 0007-setters-and-key-paths/Setters and Key Paths.playground/Contents.swift rename to 0007-setters-and-key-paths/Setters and Key Paths.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift index b45d88e9..9ed6cc88 100644 --- a/0007-setters-and-key-paths/Setters and Key Paths.playground/Contents.swift +++ b/0007-setters-and-key-paths/Setters and Key Paths.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift @@ -180,6 +180,5 @@ User.template let boringLocal = .template |> noFavoriteFoods |> domestic - - +//: [See the next page](@next) for exercises! diff --git a/0007-setters-and-key-paths/Setters and Key Paths.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift b/0007-setters-and-key-paths/Setters and Key Paths.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..57b74531 --- /dev/null +++ b/0007-setters-and-key-paths/Setters and Key Paths.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift @@ -0,0 +1,30 @@ +/*: + # Setters and Key Paths Exercises + + 1. In this episode we used `Dictionary`’s subscript key path without explaining it much. For a `key: Key`, one can construct a key path `\.[key]` for setting a value associated with `key`. What is the signature of the setter `prop(\.[key])`? Explain the difference between this setter and the setter `prop(\.[key]) <<< map`, where `map` is the optional map. + */ +// TODO +/*: + 2. The `Set` type in Swift does not have any key paths that we can use for adding and removing values. However, that shouldn't stop us from defining a functional setter! Define a function `elem` with signature `(A) -> ((Bool) -> Bool) -> (Set) -> Set`, which is a functional setter that allows one to add and remove a value `a: A` to a set by providing a transformation `(Bool) -> Bool`, where the input determines if the value is already in the set and the output determines if the value should be included. + */ +// TODO +/*: + 3. Generalizing exercise #1 a bit, it turns out that all subscript methods on a type get a compiler generated key path. Use array’s subscript key path to uppercase the first favorite food for a user. What happens if the user’s favorite food array is empty? + */ +// TODO +/*: + 4. Recall from a [previous episode](https://www.pointfree.co/episodes/ep5-higher-order-functions) that the free `filter` function on arrays has the signature `((A) -> Bool) -> ([A]) -> [A]`. That’s kinda setter-like! What does the composed setter `prop(\\User.favoriteFoods) <<< filter` represent? + */ +// TODO +/*: + 5. Define the `Result` type, and create `value` and `error` setters for safely traversing into those cases. + */ +// TODO +/*: + 6. Is it possible to make key path setters work with `enum`s? + */ +// TODO +/*: + 7. Redefine some of our setters in terms of `inout`. How does the type signature and composition change? + */ +// TODO diff --git a/0007-setters-and-key-paths/Setters and Key Paths.playground/contents.xcplayground b/0007-setters-and-key-paths/Setters and Key Paths.playground/contents.xcplayground index 63b6dd8d..5ae6818d 100644 --- a/0007-setters-and-key-paths/Setters and Key Paths.playground/contents.xcplayground +++ b/0007-setters-and-key-paths/Setters and Key Paths.playground/contents.xcplayground @@ -1,4 +1,7 @@ - - + + + + + \ No newline at end of file diff --git a/0008-getters-and-key-paths/Getters and Key Paths.playground/Contents.swift b/0008-getters-and-key-paths/Getters and Key Paths.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift similarity index 94% rename from 0008-getters-and-key-paths/Getters and Key Paths.playground/Contents.swift rename to 0008-getters-and-key-paths/Getters and Key Paths.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift index eec63b5a..b2a27ce5 100644 --- a/0008-getters-and-key-paths/Getters and Key Paths.playground/Contents.swift +++ b/0008-getters-and-key-paths/Getters and Key Paths.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift @@ -181,15 +181,4 @@ users.sorted(by: their(^\.email, >)) users.max(by: their(^\.email.count))?.email users.min(by: their(^\.email.count))?.email - -import Foundation - -//NSObject().observe(<#T##keyPath: KeyPath##KeyPath#>, changeHandler: <#T##(NSObject, NSKeyValueObservedChange) -> Void#>) - - - - - - - - +//: [See the next page](@next) for exercises! diff --git a/0008-getters-and-key-paths/Getters and Key Paths.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift b/0008-getters-and-key-paths/Getters and Key Paths.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..4d7196f3 --- /dev/null +++ b/0008-getters-and-key-paths/Getters and Key Paths.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift @@ -0,0 +1,51 @@ +/*: + # Getters and Key Paths Exercises + + 1. Find three more standard library APIs that can be used with our `get` and `^` helpers. + */ +// TODO +/*: + 2. The one downside to key paths being _only_ compiler generated is that we do not get to create new ones ourselves. We only get the ones the compiler gives us. + + And there are a lot of getters and setters that are not representable by key paths. For example, the “identity” key path `KeyPath` that simply returns `self` for the getter and that setting on it leaves it unchanged. Can you think of any other interesting getters/setters that cannot be represented by key paths? + */ +// TODO +/*: + 3. In our [Setters and Key Paths](https://www.pointfree.co/episodes/ep7-setters-and-key-paths) episode we showed how `map` could kinda be seen as a “setter” by saying: + + “If you tell me how to transform an `A` into a `B`, I will tell you how to transform an `[A]` into a `[B]`.” + + There is also a way to think of `map` as a “getter” by saying: + + “If you tell me how to get a `B` out of an `A`, I will tell you how to get an `[B]` out of an `[A]`.” + + Try composing `get` with free `map` function to construct getters that go even deeper into a structure. + + You may want to use the data types we defined [last time](https://github.com/pointfreeco/episode-code-samples/blob/1998e897e1535a948324d590f2b53b6240662379/0007-setters-and-key-paths/Setters%20and%20Key%20Paths.playground/Contents.swift#L2-L20). + */ +// TODO +/*: + 4. Repeat the above exercise by seeing how the free optional `map` can allow you to dive deeper into an optional value to extract out a part. + + Key paths even give first class support for this operation. Do you know what it is? + */ +// TODO +/*: + 5. Key paths aid us in getter composition for structs, but enums don't have any stored properties. Write a getter function for `Result` that plucks out a value if it exists, such that it can compose with `get`. Use this function with a value in `Result` to return the user's name. + */ +// TODO +/*: + 6. Key paths work immediately with all fields in a struct, but only work with computed properties on an enum. We saw in [Algebra Data Types](https://www.pointfree.co/episodes/ep4-algebraic-data-types) that structs and enums are really just two sides of a coin: neither one is more important or better than the other. + + What would it look like to define an `EnumKeyPath` type that encapsulates the idea of “getting” and “setting” cases in an enum? + */ +// TODO +/*: + 7. Given a value in `EnumKeyPath` and `EnumKeyPath`, can you construct a value in + `EnumKeyPath`? + */ +// TODO +/*: + 8. Given a value in `EnumKeyPath` and a value in `EnumKeyPath`, can you construct a value in `EnumKeyPath, C>`? + */ +// TODO diff --git a/0008-getters-and-key-paths/Getters and Key Paths.playground/contents.xcplayground b/0008-getters-and-key-paths/Getters and Key Paths.playground/contents.xcplayground index 63b6dd8d..3f1fc6f1 100644 --- a/0008-getters-and-key-paths/Getters and Key Paths.playground/contents.xcplayground +++ b/0008-getters-and-key-paths/Getters and Key Paths.playground/contents.xcplayground @@ -1,4 +1,7 @@ - - - \ No newline at end of file + + + + + +