That wasn't so bad, now was it? Now, let's take a look at my solution: ```coconut def diagonal_line(n) = range(n+1) |> map$(i -> (i, n-i)) ``` Pretty simple, huh? We take `range(n+1)`, and use `map` to transform it into the right sequence of tuples. ### `linearized_plane` Now that we've created our diagonal lines, we need to join them together to make the full linearized plane, and to do that we're going to write the function `linearized_plane()`. `linearized_plane` should produce an iterator that goes through all the points in the plane, in order of all the points in the first `diagonal(0)`, then the second `diagonal(1)`, and so on. `linearized_plane` is going to be, by necessity, an infinite iterator, since it needs to loop through all the points in the plane, which have no end. To help you accomplish this, remember that the `::` operator is lazy, and won't evaluate its operands until they're needed, which means it can be used to construct infinite iterators. When you're ready to move on, scroll down. Tests: ```coconut # Note: these tests use $[] notation, which we haven't introduced yet # but will introduce later in this case study; for now, just run the # tests, and make sure you get the same result as is in the comment linearized_plane()$[0] |> print # (0, 0) linearized_plane()$[:3] |> list |> print # [(0, 0), (0, 1), (1, 0)] ``` _Hint: instead of defining the function as `linearized_plane()`, try defining it as `linearized_plane(n=0)`, where `n` is the diagonal to start at, and use recursion to build up from there._

That was a little bit rougher than the first one, but hopefully still not too bad. Let's compare to my solution: ```coconut def linearized_plane(n=0) = diagonal_line(n) :: linearized_plane(n+1) ``` As you can see, it's a very fundamentally simple solution: just use `::` and recursion to join all the diagonals together in order. ### `vector_field` Now that we have a function that builds up all the points we need, it's time to turn them into vectors, and to do that we'll define the new function `vector_field()`, which should turn all the tuples in `linearized_plane` into vectors, using the n-vector class we defined earlier. Tests: ```coconut # You'll need to bring in the vector class from earlier to make these work vector_field()$[0] |> print # vector(*pts=(0, 0)) vector_field()$[2:3] |> list |> print # [vector(*pts=(1, 0))] ``` _Hint: Remember, the way we defined vector it takes the components as separate arguments, not a single tuple. You may find the [Coconut built-in `starmap`](./DOCS.md#starmap) useful in dealing with that._

We're making good progress! Before we move on, check your solution against mine: ```coconut def vector_field() = linearized_plane() |> starmap$(vector) ``` All we're doing is taking our `linearized_plane` and mapping `vector` over it, but using `starmap` instead of `map` so that `vector` gets called with each element of the tuple as a separate argument. ### Applications Now that we've built all the functions we need for our vector field, it's time to put it all together and test it. Feel free to substitute in your versions of the functions below: ```coconut data vector(*pts): """Immutable n-vector.""" def __new__(cls, *pts): """Create a new vector from the given pts.""" match [v `isinstance` vector] in pts: return v # vector(v) where v is a vector should return v else: return pts |*> makedata$(cls) # accesses base constructor def __abs__(self) = """Return the magnitude of the vector.""" self.pts |> map$(.**2) |> sum |> (.**0.5) def __add__(self, vector(*other_pts) if len(other_pts) == len(self.pts)) = """Add two vectors together.""" map((+), self.pts, other_pts) |*> vector def __sub__(self, vector(*other_pts) if len(other_pts) == len(self.pts)) = """Subtract one vector from another.""" map((-), self.pts, other_pts) |*> vector def __neg__(self) = """Retrieve the negative of the vector.""" self.pts |> map$(-) |*> vector def __mul__(self, other): """Scalar multiplication and dot product.""" match vector(*other_pts) in other: assert len(other_pts) == len(self.pts) return map((*), self.pts, other_pts) |> sum # dot product else: return self.pts |> map$(.*other) |*> vector # scalar multiplication def __rmul__(self, other) = """Necessary to make scalar multiplication commutative.""" self * other def diagonal_line(n) = range(n+1) |> map$(i -> (i, n-i)) def linearized_plane(n=0) = diagonal_line(n) :: linearized_plane(n+1) def vector_field() = linearized_plane() |> starmap$(vector) # Test cases: diagonal_line(0) `isinstance` (list, tuple) |> print # False (should be an iterator) diagonal_line(0) |> list |> print # [(0, 0)] diagonal_line(1) |> list |> print # [(0, 1), (1, 0)] linearized_plane()$[0] |> print # (0, 0) linearized_plane()$[:3] |> list |> print # [(0, 0), (0, 1), (1, 0)] vector_field()$[0] |> print # vector(*pts=(0, 0)) vector_field()$[2:3] |> list |> print # [vector(*pts=(1, 0))] ``` Copy, paste! Once you've made sure everything is working correctly if you substituted in your own functions, take a look at the last 4 tests. You'll notice that they use a new notation, similar to the notation for partial application we saw earlier, but with brackets instead of parentheses. This is the notation for iterator slicing. Similar to how partial application was lazy function calling, iterator slicing is _lazy sequence slicing_. Like with partial application, it is helpful to think of `$` as the _lazy-ify_ operator, in this case turning normal Python slicing, which is evaluated immediately, into lazy iterator slicing, which is evaluated only when the elements in the slice are needed. With that in mind, now that we've built our vector field, it's time to use iterator slicing to play around with it. Try doing something cool to our vector fields like - create a `magnitude_field` where each point is that vector's magnitude - combine entire vector fields together with `map` and the vector addition and multiplication methods we wrote earlier then use iterator slicing to take out portions and examine them. ## Case Study 5: `vector` Part II For the some of the applications you might want to use your `vector_field` for, it might be desirable to add some useful methods to our `vector`. In this case study, we're going to be focusing on one in particular: `.angle`. `.angle` will take one argument, another vector, and compute the angle between the two vectors. Mathematically, the formula for the angle between two vectors is the dot product of the vectors' respective unit vectors. Thus, before we can implement `.angle`, we're going to need `.unit`. Mathematically, the formula for the unit vector of a given vector is that vector divided by its magnitude. Thus, before we can implement `.unit`, and by extension `.angle`, we'll need to start by implementing division. ### `__truediv__` Vector division is just scalar division, so we're going to write a `__truediv__` method that takes `self` as the first argument and `other` as the second argument, and returns a new vector the same size as `self` with every element divided by `other`. For an extra challenge, try writing this one in one line using assignment function notation. Tests: ```coconut vector(3, 4) / 1 |> print # vector(*pts=(3.0, 4.0)) vector(2, 4) / 2 |> print # vector(*pts=(1.0, 2.0)) ``` _Hint: Look back at how we implemented scalar multiplication._

Here's my solution for you to check against: ```coconut def __truediv__(self, other) = self.pts |> map$(x -> x/other) |*> vector ``` ### `.unit` Next up, `.unit`. We're going to write a `unit` method that takes just `self` as its argument and returns a new vector the same size as `self` with each element divided by the magnitude of `self`, which we can retrieve with `abs`. This should be a very simple one-line function. Tests: ```coconut vector(0, 1).unit() |> print # vector(*pts=(0.0, 1.0)) vector(5, 0).unit() |> print # vector(*pts=(1.0, 0.0)) ```

Here's my solution: ```coconut def unit(self) = self / abs(self) ``` ### `.angle` This one is going to be a little bit more complicated. For starters, the mathematical formula for the angle between two vectors is the `math.acos` of the dot product of those vectors' respective unit vectors, and recall that we already implemented the dot product of two vectors when we wrote `__mul__`. So, `.angle` should take `self` as the first argument and `other` as the second argument, and if `other` is a vector, use that formula to compute the angle between `self` and `other`, or if `other` is not a vector, `.angle` should raise a `MatchError`. To accomplish this, we're going to want to use destructuring assignment to check that `other` is indeed a `vector`. Tests: ```coconut import math vector(2, 0).angle(vector(3, 0)) |> print # 0.0 print(vector(1, 0).angle(vector(0, 2)), math.pi/2) # should be the same vector(1, 2).angle(5) # MatchError ``` _Hint: Look back at how we checked whether the argument to `factorial` was an integer using pattern-matching._

Here's my solution—take a look: ```coconut def angle(self, other `isinstance` vector) = math.acos(self.unit() * other.unit()) ``` And now it's time to put it all together. Feel free to substitute in your own versions of the methods we just defined. ```coconut import math # necessary for math.acos in .angle data vector(*pts): """Immutable n-vector.""" def __new__(cls, *pts): """Create a new vector from the given pts.""" match [v `isinstance` vector] in pts: return v # vector(v) where v is a vector should return v else: return pts |*> makedata$(cls) # accesses base constructor def __abs__(self) = """Return the magnitude of the vector.""" self.pts |> map$(.**2) |> sum |> (.**0.5) def __add__(self, vector(*other_pts) if len(other_pts) == len(self.pts)) = """Add two vectors together.""" map((+), self.pts, other_pts) |*> vector def __sub__(self, vector(*other_pts) if len(other_pts) == len(self.pts)) = """Subtract one vector from another.""" map((-), self.pts, other_pts) |*> vector def __neg__(self) = """Retrieve the negative of the vector.""" self.pts |> map$(-) |*> vector def __mul__(self, other): """Scalar multiplication and dot product.""" match vector(*other_pts) in other: assert len(other_pts) == len(self.pts) return map((*), self.pts, other_pts) |> sum # dot product else: return self.pts |> map$(.*other) |*> vector # scalar multiplication def __rmul__(self, other) = """Necessary to make scalar multiplication commutative.""" self * other # New one-line functions necessary for finding the angle between vectors: def __truediv__(self, other) = self.pts |> map$(x -> x/other) |*> vector def unit(self) = self / abs(self) def angle(self, other `isinstance` vector) = math.acos(self.unit() * other.unit()) # Test cases: vector(3, 4) / 1 |> print # vector(*pts=(3.0, 4.0)) vector(2, 4) / 2 |> print # vector(*pts=(1.0, 2.0)) vector(0, 1).unit() |> print # vector(*pts=(0.0, 1.0)) vector(5, 0).unit() |> print # vector(*pts=(1.0, 0.0)) vector(2, 0).angle(vector(3, 0)) |> print # 0.0 print(vector(1, 0).angle(vector(0, 2)), math.pi/2) # should be the same vector(1, 2).angle(5) # MatchError ``` _One note of warning here: be careful not to leave a blank line when substituting in your methods, or the interpreter will cut off the code for the `vector` there. This isn't a problem in normal Coconut code, only here because we're copy-and-pasting into the command line._ Copy, paste! If everything is working, you can try going back to playing around with `vector_field` [applications](#applications) using our new methods. ## Filling in the Gaps And with that, this tutorial is out of case studies—but that doesn't mean Coconut is out of features! In this last section, we'll touch on some of the other useful features of Coconut that we managed to miss in the case studies. ### Lazy Lists First up is lazy lists. Lazy lists are lazily-evaluated lists, similar in their laziness to Coconut's `::` operator, in that any expressions put inside a lazy list won't be evaluated until that element of the lazy list is needed. The syntax for lazy lists is exactly the same as the syntax for normal lists, but with "banana brackets" (`(|` and `|)`) instead of normal brackets, like so: ```coconut abc = (| a, b, c |) ``` Unlike Python iterators, lazy lists can be iterated over multiple times and still return the same result. Unlike Python lists, however, using a lazy list, it is possible to define the values used in the following expressions as needed without raising a `NameError`: ```coconut abcd = (| d(a), d(b), d(c) |) # a, b, c, and d are not defined yet def d(n) = n + 1 a = 1 abcd$[0] b = 2 abcd$[1] c = 3 abcd$[2] ``` ### Function Composition Next is function composition. In Coconut, this is primarily accomplished through the `f1 ..> f2` operator, which takes two functions and composes them, creating a new function equivalent to `(*args, **kwargs) -> f2(f1(*args, **kwargs))`. This can be useful in combination with partial application for piecing together multiple higher-order functions, like so: ```coconut zipsum = zip ..> map$(sum) ``` _While `..>` is generally preferred, if you'd rather use the more traditional mathematical function composition ordering, you can get that with the `<..` operator._ If the composed functions are wrapped in parentheses, arguments can be passed into them: ```coconut def plus1(x) = x + 1 def square(x) = x * x (square ..> plus1)(3) == 10 # True ``` Functions of different arities can be composed together, as long as they are in the correct order. If they are in the incorrect order, a `TypeError` will be raised. In this example we will compose a unary function with a binary function: ```coconut def add(n, m) = n + m # binary function def square(n) = n * n # unary function (square ..> add)(3, 1) # Raises TypeError: square() takes exactly 1 argument (2 given) (add ..> square)(3, 1) # 16 ``` Another useful trick with function composition involves composing a function with a higher-order function: ```coconut def inc_or_dec(t): # Our higher-order function, which returns another function if t: return x -> x+1 else: return x -> x-1 def square(n) = n * n square_inc = inc_or_dec(True) ..> square square_dec = inc_or_dec(False) ..> square square_inc(4) # 25 square_dec(4) # 9 ``` _Note: Coconut also supports the function composition operators `..`, `..*>`, `<*..`, `..**>`, and `<**..`._ ### Implicit Partials Another useful Coconut feature is implicit partials. Coconut supports a number of different "incomplete" expressions that will evaluate to a function that takes in the part necessary to complete them, that is, an implicit partial application function. The different allowable expressions are: ```coconut .attr .method(args) obj. func$ seq[] iter$[] .[slice] .$[slice] ``` For a full explanation of what each implicit partial does, see Coconut's documentation on [implicit partials](./DOCS.md#implicit-partial-application). ### Type Annotations For many people, one of the big downsides of Python is the fact that it is dynamically-typed. In Python, this problem is addressed by [MyPy](http://mypy-lang.org/), a static type analyzer for Python, which can check Python-3-style type annotations such as ```coconut_python def plus1(x: int) -> int: return x + 1 a: int = plus1(10) ``` Unfortunately, in Python, such type annotation syntax only exists in Python 3. Not to worry in Coconut, however, which compiles Python-3-style type annotations to universally compatible type comments. Not only that, but Coconut has built-in [MyPy integration](./DOCS.md#mypy-integration) for automatically type-checking your code, and its own [enhanced type annotation syntax](./DOCS.md#enhanced-type-annotation) for more easily expressing complex types, like so: ```coconut def int_map( f: int -> int, xs: int[], ) -> int[] = xs |> map$(f) |> list ``` ### Further Reading And that's it for this tutorial! But that's hardly it for Coconut. All of the features examined in this tutorial, as well as a bunch of others, are detailed in Coconut's [documentation](./DOCS.md). Also, if you have any other questions not covered in this tutorial, feel free to ask around at Coconut's [Gitter](https://gitter.im/evhub/coconut), a GitHub-integrated chat room for Coconut developers. Finally, Coconut is a new, growing language, and if you'd like to get involved in the development of Coconut, all the code is available completely open-source on Coconut's [GitHub](https://github.com/evhub/coconut). Contributing is a simple as forking the code, making your changes, and proposing a pull request! See Coconuts [contributing guidelines](./CONTRIBUTING.md) for more information.