PurelyFunctional.tv Newsletter 396: how types help you reason
Issue 396 - September 21, 2020 · Archives · Subscribe
Clojure Tip 💡
how types help you reason
I was listening to Gene Kim talk to Michael Nygard on The Idealcast. They were talking about how polymorphic types limit what you can do, which means you are more sure about what it will do. This needs an example.
foo :: [Int] -> Int
This is a Haskell type signature. It says the function foo
takes a
list of integers and returns an integer. What does this foo
function
do? Does it sum the numbers in the list? Does it multiply them? Does it
return the average? Or the largest? Or the first? There are a ton of
things it can do, so your uncertainty about what it does is high.
Now, let's make it less specific so that it works on a list of any type. Still returns an integer.
foo :: [a] -> Int
This type signature says foo
takes a list of any type and returns an
integer. Values of any one type can be in that list, instead of
integers. The counterintuitive thing is that we know less about the
values being passed in, but we know more about what the function does.
How is that?
Let's think about what this function could do. Well, we don't know what type is actually in the list, so there's basically nothing we can do with those values. The only thing we do know is that it's a list, and really the only thing you can do with a list that returns an integer is count the elements.
Well, I say the only thing, but that's not quite true. You could do
something silly, like return a constant integer, regardless of the value
passed in. Or you could do something like multiply the length of the
list by 2
. Or something weirder like return 0
if the length is
non-prime, and the length if it is prime. But those are not very likely,
and length is very likely, so we are more sure about what it does than
when we know more about the type.
That's a really interesting feature of type systems. It's saying that more general functions (functions that take less specific types) are easier to implement and understand. And the type system helps prove that. There are literally fewer expressions that will be accepted by the compiler. It's one of those benefits of static types that goes well beyond preventing errors.
This idea got me thinking about how it applies to functions and maps in Clojure. Why is it that we prefer "open" maps? But that will have to wait till next week's issue.
Quarantine update 😷
I know a lot of people are going through tougher times than I am. If you, for any reason, can't afford my courses, and you think the courses will help you, please hit reply and I will set you up. It's a small gesture I can make, but it might help.
I don't want to shame you or anybody that we should be using this time to work on our skills. The number one priority is your health and safety. I know I haven't been able to work very much, let alone learn some new skill. But if learning Clojure is important to you, and you can't afford it, just hit reply and I'll set you up. Keeping busy can keep us sane.
Also, if you just want to subscribe for a paid membership, I have opened them back up for the moment. Register here.
Stay healthy. Wash your hands. Stay at home. Wear a mask. Take care of loved ones.
Clojure Challenge 🤔
Last week's challenge
- Seasons - submissions
Lots of great discussion about different ways to represent the table of the seasons.
Please do participate in the discussion at the submission links above. It's active and it's a great way to get comments on your code.
This week's challenge
Big change: Please submit your solutions as comments on the gist linked below.
Set Game
I used to play a game called Set. In it, you'd place 12 cards down on the table. Then the players would try to find "sets". If you found a set, you'd race to hit the cards with your hand to claim it. It was tons of fun and just mathy enough to appeal to someone like me.
In this task, you are going to take a function that judges whether three cards constitute a valid "set".
Each card has four properties:
- Color (red, purple, green)
- Number (1, 2, 3)
- Shading (empty, lined, full)
- Shape (squiggle, oval, diamond)
So one card might have 3 purple full diamonds. Search for the game and look at pictures if you want some examples.
We'll represent each card as a map, like this:
{:color :purple
:number 3
:shading :full
:shape :diamond}
Three cards form a set if all of the properties are total matches or total mismatches. A property is a total match if all of the values are the same, for instance three red cards. They are a total mismatch if all the values are different, for instance a diamond, an oval, and a squiggle. If any of the properties don't match or mismatch, it's not a set.
Here's an example of a set:
[{:color :purple :number 3 :shape :diamond :shading :full}
{:color :red :number 3 :shape :diamond :shading :lines}
{:color :green :number 3 :shape :diamond :shading :empty}]
Colors are a total mismatch, numbers are a total match, shape is a total match, shading is a total mismatch.
Here's an example of a non-set:
[{:color :purple :number 3 :shape :diamond :shading :full}
{:color :red :number 3 :shape :diamond :shading :lines}
{:color :purple :number 3 :shape :diamond :shading :empty}]
Above, the colors are two purples and a red. Not a total match and not a total mismatch.
Write a function that takes an array of cards and says whether they are a set.
(set? [{..} {..} {..}]) ;=> true/false
Thanks to this site for the challenge idea where it is considered Very Hard level in JavaScript.
Please submit your solutions as comments to this gist. D iscussion is welcome.
Big change: Please submit your solutions as comments on the gist linked above.
Rock on!
Eric Normand