447 karma · joined November 20, 2011
[ my public key: https://keybase.io/chas; my proof: https://keybase.io/chas/sigs/3e4ZitlD0tm8a9ZVzI7rkK5G03snHd2qvx2ABwqOtHI ]
The case is exactly the same for the Clean language as for Haskell and indeed Clean has Monad instances for all of the types I mentioned aside from STM, so there are no concerns with dealing with those monadic abstractions in Clean. (https://imgur.com/a/sjsDiZq, https://cloogle.org/#using%20Monad) Since IO is Haskell's type that marks impure computations, we can compare Haskell's implementation to the equivalent one in Clean (interface: https://cloogle.org/src/#Platform/System/IO;line=10, implementation: https://cloogle.org/src/#Platform/System/IO;icl)
In Clean, IO is implemented as follows:
:: IO a = IO .(*World -> *(a, !*World))
In Haskell, it is: newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
(for full context see: http://hackage.haskell.org/package/ghc-prim-0.5.3/docs/src/G..., the related monad instance is here: http://hackage.haskell.org/package/base-4.12.0.0/docs/src/GH...)These both implement IO as a function which takes the state of the world as its input and returns a new state of the world as it's output. This is encoded using the state transformation that I mention in the other post (https://acm.wustl.edu/functional/state-monad.php) Both implementations also go on to define Monad instances for their new IO type. The major difference is that the Haskell standard library only exposes bindIO and returnIO to the user and hides the internals of IO from the user and Clean allows the implementation to be a normal library.
That difference is Clean's uniqueness types showing their strength. Clean can explicitly expose it's predefined World type (https://cloogle.org/doc/#CleanRep.2.2_6.htm;jump=_Toc3117980...) to the user with the guarantee that you can't write a function of type IO a -> a because it would violate the uniqueness properties and thus be a compilation error. Haskell instead uses the module system to keep State# RealWorld from being exposed to the user. This means that if you as a user want a different set of abstractions for impurity in Clean, you can build from the World level rather than needing to construct it out of what can be done with bindIO and returnIO. For details on what's going on with State# see https://www.fpcomplete.com/blog/2015/02/primitive-haskell
From the perspective of "the value prop for learning/using monads", this discussion leaves us in a worse place than where we started because the conclusion is that uniqueness types aren't a get out of monads free card and that Clean uses the many of the same Monad-related abstractions as Haskell does and uses them for the same purposes. In order to not leave you out in the cold as to the value prop for the monad abstraction, you can see how it works for a number of different Monad instances in Tikhon's answer on Quora (https://www.quora.com/What-are-monads-in-functional-programm...), though he chooses to use join, fmap, and return as the fundamental parts of a Monad, rather than bind and return, as I have here. As he touches on in his discussion, it's common and straightforward to implement one definition in terms of the other, so anything you learn from about that definition can be ported the definition I use without much fuss, so don't worry if it doesn't match at first. What this means is that if you have a tools in the standard library that only depend on features of Monad, you can use the same small collection of functions to solve a ton of problems.
I think focusing on the "monad" part over the "IO" part of "monadic IO" is particularly confusing to new users because the abstract idea of a monad is very general, so if you assume all places where it shows up are basically like the case of IO, you will be very confused. Further, it makes the idea of a monad seem like a Haskell-specific hack, rather than a general abstraction that can be used in any programming language you want to.
This is particularly important to emphasize because the abstract idea of monads only makes the IO approach to impurity nice to use, it doesn't make it possible. Haskell had I/O (and other impure capabilities) before the monadic way of organizing impure code was introduced. The heavy lifting for IO is done by having a type system strong enough to prevent a function of type IO a -> a from being written by an end-user. If you have written a monad abstraction in a language without such a type system[0], it can still be a nice abstraction, but it doesn't guarantee that pure and impure computations can be distinguished on the type level.
[0] https://www.nurkiewicz.com/2016/06/functor-and-monad-example...
Monads aren't the separation between purely functional and stateful code. The Haskell type system maintains that separation. Anything that's doesn't return IO a for some a appears to be a pure function from the perspective of the programmer. Once a function returns IO a, there aren't any* functions provided by the compiler that can make a function that uses those results not also return IO b for some b. For example, the type of getLine is IO String (because it impurely produces a String) and the type of putStr is String -> IO () (because it takes a String and mutates the world without returning anything).
If the compiler provided a function for computing on the a in the IO a, for instance, bindIO :: IO a -> (a -> IO b) -> IO b and a function to wrap the results of non-IO functions, such as returnIO :: a -> IO a, you could do arbitrary computation with these IO-wrapped data types, but know at a glance if your functions were impure.
This approach doesn't require the Monad typeclass at all, just a magic type called IO that tags impure computations that are implemented with compiler and runtime magic. It happens to be the case that this is exactly how GHC implements the IO type. bindIO is implemented here[0] and returnIO is implemented here[1] and the compiler magic used to implement them isn't* exported, so all IO operations have to go through those functions. It is not a coincidence to that these functions have the right types to form a Monad instance for IO and indeed, that is also present[2], but the IO type and the type system that ensures it can't be sneakily hidden are doing the heavy lifting, and the Monad instance (and accompanying syntactic sugar), are just there to make it nicer to work with and easier to abstract over.
If you have a passing familiarity with Haskell, the phrase "state monad" is the obvious place where my claims stop making sense. In fact, the State type only supports computations that are entirely pure. If you want to simulate global variables in a language that didn't have them, you could always pass all of your global variables to every function and get updated ones back from the function along with the nominal results of the computation. The State type is just a regular data type that wraps stateful functions constructed by such state passing. A type of the form State Int String is just a function that takes an Int and returns and String and an Int, no compiler or runtime magic needed.
You can play the same trick as in the IO case and provide functions bindState :: State s a -> (a -> State s b) -> State s b and returnState :: a -> State s a in order to compute on these "stateful" values while making sure the result state got passed to the next function in the chain correctly. Like IO these two functions can be used to create a Monad instance for State. Unlike IO, State is just a data type holding a regular Haskell function, so it's extremely reasonable to write a function of type State s a -> s -> a which runs the State s a computation with an initial value of type s. This is written by unwrapping the State type and then passing the initial state value to the function inside and return the result while ignoring the returned new state. More details on how State is implemented are available here[3].
A complication to this is that if you want stateful mutation for performance reasons, the ST type[4] also exists, which looks identical to the State type from the programmer's perspective, but plays similar tricks to IO in order to actually mutate under the hood while not exposing the implementation details to the user, so it can be reasoned about exactly as if it was pure and using the same implementation as State.
These Monad instances for IO, State, and ST start to pull their weight when you write functions that only use features provided by the Monad typeclass and they work seamlessly with any implementation of stateful computation despite their very different internals. Monad is quite general, so if all you care about is abstracting over stateful computations, you can also use the methods from MonadState[5] which allow you to interact with the state along with the results of the computation independent of the implementation of stateful computation.
* In the name of not getting bogged down in details, there are a few parts of this discussion that are not entirely accurate, particularly around functions like unsafePerformIO[6].
[0] http://hackage.haskell.org/package/base-4.12.0.0/docs/src/GH...
[1] http://hackage.haskell.org/package/base-4.12.0.0/docs/src/GH...
[2] http://hackage.haskell.org/package/base-4.12.0.0/docs/src/GH...
[3] https://acm.wustl.edu/functional/state-monad.php
[4] http://hackage.haskell.org/package/base-4.12.0.0/docs/Contro...
[5] http://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Mo...
[6] http://hackage.haskell.org/package/base-4.12.0.0/docs/System...
On that front, the Monad typeclass is far more general and useful than just for IO and State, so if you are thinking of it as primarily a hack to deal with those, you probably won't get the hype. In addition, it's really useful to work with a large number of examples of different Monad instances (IO, State, Maybe, List, STM[1] if want to get a bit further into the deep end) instead of just staring at the methods in the typeclass and hoping it make sense. It's a pretty broad abstraction, so it will only make sense if you are familiar with what it's abstracting.
[0] https://wiki.haskell.org/Typeclassopedia
[1] http://book.realworldhaskell.org/read/software-transactional...
It is primarily meant for helping people understand how the abstractions work, rather than make an argument for when they are good to use, but it might give you enough background to understand the discussion around Haskell abstractions.
[0] https://www.youtube.com/watch?v=kYB8IZa5AuE
[1] https://en.wikipedia.org/wiki/Covariance_and_contravariance_...
Do you have particular application areas or problems you want to apply ML to?
You'll note in those calculations that he assumes that the weld is basically the same as the base metal. As long as the weld is performed properly, the assumptions should hold to within a factor of safety.
For the relatively thin material used in cars and motorcycles, it doesn't take very long for the metal to melt all the way through in a single spot, so very strong welds can be created quickly. Further, between the skill of the fabricators and editing for TV, it is hard for someone without familiarity with welding to know what all is going into a joint just by watching the footage after.
Do you have a specific show in mind?
There is nothing you can comcatenate to a list to get the empty list, nothing you can OR with True to get False, and nothing you can union with a set to get the empty set.
If this is interesting to you Dan Piponi goes into significantly more detail (http://blog.sigfpe.com/2009/01/haskell-monoids-and-their-use...).
[0] https://news.ycombinator.com/item?id=13803843 [1] https://www.cs.ox.ac.uk/files/3395/PRG72.pdf
They emphasize that Dreamcatcher uses a "top-down" style of design, so maybe they are using deep learning for NLP to parse requirements and then feeding those requirements into normal topological optimization tools?
[0] https://ti.arc.nasa.gov/m/pub-archive/1244h/1244%20(Hornby).... (pictures from their post on page 5)
If you are particularly interested in domino logic and adders, baddox posted this video (https://www.youtube.com/watch?v=SudixyugiX4), petercooper posted this one (https://www.youtube.com/watch?v=lNuPy-r1GuQ) by Matt Parker (the same guy in this video) and I documented a 2-bit build I did (http://imgur.com/a/qq7Kl).
[0] http://covariant.me/notes/rep-functors.html [1] https://pamiz.wordpress.com/2014/02/13/the-functor-of-infini...