526 karma · joined December 30, 2010
Website: https://helbl.ing/
My girlfriend and I stayed at a bead & breakfast in the new city. Most of the tourists are locals from France, but enough people speak English that communication is not a problem.
If you haven't used algebraic/sum types before, I highly recommend trying them out. Once you use them you won't want to go back.
Here's the situation that I'm thinking about: it's well known that we've come close to nuclear annihilation multiple times, either by accident or by tensions between the US and the USSR in the Cold War. If the MWI is correct, it is probable that a good chunk of these worlds have been reduced to ash. However, we currently find ourselves in a world where that has not happened, presumably due to the anthropic principle. Could we then give a numerical probability that MWI is correct, based on this fact?
I'm not a philosopher or physicist - just an interested layman.
As you mentioned, the memory size and the program size are the biggest two constraints. There's no space for a garbage collector, and everything has to be allocated on the stack. It turns out that it is possible to make the entire thing stack-free. The two biggest hurdles are function closures, arrays, and recursive data types.
Function closures are typically allocated on the heap since it's possible to return functions from other functions. The solution is to include the closure as part of the function type, which means that they can then be allocated on the stack. Arrays are a bit more tricky to allocate on the stack. My solution is to statically size arrays with type level natural numbers, which means that the sizes are known at compile time.
Recursive datatypes are a problem that I have not dealt with yet. In most Arduino projects recursive datatypes are not really used, so I just don't allow them. It might be possible to use type level natural numbers to put a bound on recursion depth. Another idea I had was to use run-timed size datatypes. For example, let's say you have a function F1 which calls F2. The frame pointers for their corresponding frames are at S1 and S2. When F2 returns a value of size N, the solution is to copy the value to a scratch space, return to F1, decrement the frame pointer, and then copy the value back into the stack frame. I'm not sure if this is really suitable for embedded systems since it's a lot of code/machinery which takes up precious program space.
Rust is a great language which can work around these issues due to its linear types. I'm looking forward to using Rust once it starts to support the Arduino microcontrollers.
Same with the train - I use a website that shows the exact locations of all the MBTA trains, and I am able to accurately predict how long the train will sit outside of the Alewife station (which is infuriating BTW) based on the number of trains currently at the platform. A prediction would not be able to take into account this domain knowledge and would be completely worthless.
Haskell has a feature called type classes which allows you to overload functions. The definition of a type class gives a list of functions with their type signatures and names. When you write an instance of a type class, you must give implementations for the functions defined in the type class. All the functions that you define in the instance must match the type signature given in the type class definition.
Understand kinds is another critical prerequisite. Have you ever wondered what the type of a type is? Kinds are a simple construction that proves to be useful. Just as expressions have a type signature, a type expression has a kind signature. In its most basic form, a kind tells us how we can construct a type. We represent kinds by using asterisks * and kind functions ->. The asterisk is pronounced as "type".
The easiest way to understand kinds is by looking at a bunch of examples of types and type constructors. Monomorphic types such as Int and Bool have kind * . Type constructors are handled differently. An example of a type constructor is [] (list), which has kind * -> * . So list is a type constructor that takes in a type (which we represent with an asterisk), and returns another type. Therefore [Int] has kind * , since we applied the type Int to the list type constructor [], resulting in the type [Int]. Types constructors can also in some situations be partially applied, just like value constructors. Kinds are right associative, so the kind * -> * -> * is the same as * -> ( * -> * )
Here is a table of some types in Haskell with their kinds:
+---------------------------------------+-----------------------+
| Int | * |
+---------------------------------------+-----------------------+
| Bool | * |
+---------------------------------------+-----------------------+
| Maybe | * -> * |
+---------------------------------------+-----------------------+
| Either | * -> * -> * |
+---------------------------------------+-----------------------+
| [] (list type) | * -> * |
+---------------------------------------+-----------------------+
| -> (function type) | * -> * -> * |
+---------------------------------------+-----------------------+
| a -> b | * |
+---------------------------------------+-----------------------+
| [Either Int Bool] | * |
+---------------------------------------+-----------------------+
| Either Int | * -> * |
+---------------------------------------+-----------------------+
| (->) a | * -> * |
+---------------------------------------+-----------------------+
| (,,,) (four element tuple) | * -> * -> * -> * -> * |
+---------------------------------------+-----------------------+
| Mu where | ( * -> * ) -> * |
| newtype Mu f = In { out :: f (Mu f) } | |
+---------------------------------------+-----------------------+
Now we are ready to look at the type class definition for monad: class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
On the first line, we give the name of the type class: Monad. This type class will be parameterized by a parameter named m. Based on how m is used in the function signatures, we deduce that m must have kind * -> * . This means that when we define an instance of the Monad type class, we tell what m should be. And m can be anything, as long as it has the kind * -> * ! In fact if we look at the table above, we can see that Maybe and the List type constructors have the required kind. If you've read any monad tutorials, these are often used as simple examples of monad instances.The most important function in the monad type class is >>=, so let's focus on that. >>= is an infix function with two parameters, one of type "m a" and the other of type "a -> m b". The return type of >>= is "m b". So if we were to define a monad instance for m=Maybe, the type of that particular overloaded version of >>= has to be "Maybe a -> (a -> Maybe b) -> Maybe b". What should the definition of >>= be? Well it can be anything as long as the type signatures match up!
Take a look at the second parameter that has type "a -> m b". You can think of this as a sort of callback or continuation function. Typically the >>= function takes the first parameter (which has type "m a"), does some processing to unwrap the value, passes this value to the callback function and then takes this result and does some more processing before returning the result (which must have type "m b"). The >>= can call the callback function as many times as it wants (or maybe not at all). It could do anything, as long as the type signature matches up. There's one more detail that I've so far been ignoring: the overloaded functions need to follow some rules beyond the type signature constraint. These are called the monad laws, and you can find out more about them elsewhere by searching for "monad laws".
Why even bother with the Monad type class at all? It turns out that programmers have discovered many different programming patterns that seem to match up with these signatures. In fact, this particular pattern has become observed to be so common and useful that the authors of Haskell decided to provide some useful built in language operations to make them easier to use (do notation in Haskell). You can think of the >>= as a sort of programmable semicolon, where the overloaded bind operator is parameterized by a specific continuation function, but the overall program flow is dictated by the specific overloaded version of >>=.
For example, if an airplane is oriented in the direction (x,y,z) at a rotation θ around that axis, then its rotation in quaternion form is cos(θ/2)+x sin(θ/2) i + y sin(θ/2) j + z sin(θ/2) k
Notice that the multiplications involving x, y, and z are just scaling the vector.
The other important thing to realize is that sin(θ/2) is greater than or equal to zero in the interval [0, 2*pi]. So a quaternion is just a look vector where the magnitude of the vector is determined by the rotation around the look vector axis.
See this page for a useful picture: http://www.chrobotics.com/library/understanding-quaternions
Detection is made more difficult by the fact that some players are actually really good. I've been playing Fortnite for only a couple weeks now on the Switch, and I can easily kill ~60% of players. I'm sure that with more practice I could get within the top three pretty consistently.