These kinds of expressions are trivial to do in JS. I've treated predicates in this sort of way in JS probably a couple hundreds of times. You can even implement a pipe() like haskell's "do" with like three LOCs. Although unlike lisp you can't define your own syntax to go along with the implementation :((( , but i don't think haskell can do so either.
@@MrRedstonefreedomyou have monoids in js? And not sure how do is relevant or how any of this is "trivial" when the average js dev can't even go outside without a helmet
"that looks like something and average javascript programmer would write and is extremely lame" lmfao this is why I watch your videos (oh and yeah I work with JS for a living)
You could just create a function "f = (or .) . sequence" This is a function that when given a list of functions of type [Char -> Bool] it returns a single function of type Char -> Bool. This uses Monads by pulling "Char ->" outside of the list and returning a list of [Bool], which then uses "or" to see if one is true. Or, to get it all in one line: isForbidden = or . sequence [isBraille, isDigit, isUpper]
@@noomade Due to lazy evaluation the code (isForbidden = or . sequence [isBraille, isDigit, isUpper]) should work out-of-the-box (i.e. w/o compiler optimization) quite straight.
following the logical sequence of your thought to arrive at the final composite function of all predicates using monoids was amazing, very enlightening so thanks a lot :) !
I knew about such a powerful tool as monoids before but I didn't know that any function that returns a monoid is also a monoid and it can be used in a very predictable way. Thank you.
If I ever pickup haskell, I saved this video since it's the fastest whirlwind tour of almost every aspect of the language environment 😂 -- in under 10 minutes! help system, errors, composition, libraries, it's got it all
Apart from the other nice solutions or variations that have been outlined here in the comments, I wanted to mention that there is no need to fmap a newtype data constructor just to satisfy the type checker, given that newtypes are a no-op at runtime. All newtype instances are coercible with their underlying type, so you should be able to use the ```coerce``` function from Data.Coercible to turn them into Any or All with no runtime cost. You probably need some type signature to clarify which instance to coerce into. Something like ```(coerce :: [a -> Bool] -> [a -> Any])``` or better yet ```isForbidden = getAny . (fold $ coerce predicates :: Char -> Any) where ...```
@@kinjalbasu1999 Reduction in lines of code is not a good argument. A program reduced. to a few lines of cryptic symbols and unfathomable keywords becomes as readable/understandable as a text file being zipped. The ability to add more conditions easily is not a good argument, we could do that with a list of functions in almost any language.
@@Heater-v1.0.0well i think this monoid has been used widely in http framework, which is middleware. Adding more middleware easily in form of list in our handler is big
@@thoriqadillah7780 Indeed. I think I start to get it then. This "monoid" thing is what C programmers call a a "call back". After all web servers have been allowing for the insertion of middleware since forever and I don't recall that ever being called a monoid. Luckily we don't need to use Haskel to make use of such a pattern. Almost any language will do.
hmmm in a sense yes, but not really, because is the generalization of programming patterns, that allows you for easier refactorings, at least in my experience
I understand that a monoid is a function that takes type T, T as input and returns type T as output, and I understand how it is thus necessary to give a fold/reduce/whatever higher order function a monoid. I did not understand this at first, but a second watch cleared things up! Here is my understanding, in my own words. The mathematical concept of a "boolean" is clearly a monoid. But haskell is a strongly typed language! And the haskell type Bool is NOT a monoid. Luckily, there exists a class that fixes this problem. It effectively extends Bool. (Really, it wraps it, but I don't know Haskell so idk if you can extend classes. You can't in C++, you can in something like ruby. I'm being too imprecise to be nuanced here...). And it extends Bool in a way s.t. it is a monoid! So you can use it in fold. Thus, you need to map to wrap so you can fold then unwrap to get your answer. GG
A monoid is a mathematical object. It's a pair of set and a binary operation, that satisfies certain properties. The operation needs to be closed (ie. it always returns members of the set, when it has members as the inputs) and there's neutral element (ie. there's an element for which applying the operation to any other member, just gives back that member). Examples of monoids: Integers with addition - neutral element is 0. Integers with multiplication - neutral element is 1. lists with concatenation - neutral element is empty list. Boolean on its own is not a monoid, because it is not specified which operation is should be monoid to. Both logical AND and OR form a monoid over Boolean (just like integers are monoid under both addition and multiplication). This is where traditional OOP really sucks at expressing these concepts. It reinvents the wheel, but makes it square and puts the axis at an angle. A monoid can be thought of as a virtual class, that has one virtual field (the data type you are monoiding over) and a virtual function with two inputs and output, all of the Self class (this is the binary operation).
...and in haskell you can use the same concept: `isForbidden char = any (\p-> p char) [isUpper, isNumber]`. And then you can further simplify, which does not work in JS: `isForbidden char = any ($ char) [isUpper, isNumber]`. ;)
It seems that a lot of the point of Haskell is to learn more and more abstract concepts to write code that's a little shorter, or a little weirder. I like it, but I feel that if I wrote code like this, I should probably be fired.
Actually, the examples here aren't great to understand the beauty of functional programming. In practice, the code is way shorter, and you chose if you want to make it even shorter by requiring reviewers more intimate knowledge of the language or not.
you way overdid it, all you needed was import Data.Char isForbidden :: Char -> Bool isForbidden c = any (\f -> f c) predicates where predicates = [isLower, isDigit] or to be slightly shorter any ($ c) predicates
@@noomade ah didn't see someone else used applicative and posted another comment. OPs code compiles, but your code doesn't. I'm curious what you did with the lambda after the `or`, haven't seen that before. I just know this way: `isForbidden c = or $ () [c] [isUpper, isLower]`. btw, works also without importing Control.Applicative and pointfree like so: `isForbidden = or . () [isUpper, isAlpha] . (:[])`.
@@noomade ah right, now it makes sense! the code works now. thought this was some kind of special usage of `or` with a lambda. thanks for your reply anyway.
You could achieve the same result with: isForbidden c = any ($ c) predicates I'm not sure which approach I prefer however, the monoid approach is pretty cool.
This is a bit more complicated. Since you are actually combining functions that detect characters, not just character themselves. So while it doesn't make sense I'm this context, it will be easy to extend with mor abstract predicates (Compilers and parsers written in languages like Haskell make heavy use of this kind of things) So while seemingly complicated this is actually very generic and elegant! And Haskell tends to reward elegance with Maintainability and Performance increases But yeah, I totally get that the barrier of entry is absurdly hard. And if you have no good reason to learn Haskell I would simply not recommend it. You will speed a looooot of time with very little in return
Can you please do a video on Monad Transformers and Free Monads? Please! Pretty Please! You simply are the best Haskell teacher I have ever come across.
I suspect this is "doing it for the sake of doing it because it is clever". "isDigit x || isUpper x" is much more readable that using the Monoid. If you really want to be "pointless" you could also do the slightly less obvious "(||) isDigit isUpper". Just because you can, doesn't necessarily mean you should.
I appreciate the explanation, it's very cool to see! Though much the same is possible in any other language, without the needless mathematics terminological specificity. You maligned javascript for example, but this is a trivial implementation in javascript. Functions are objects and so you can likewise describe a "noneOfAnyOfPredicates(particular Predicates)" and simply define the predicates by virtue of a constant value type. Compositionality is not a new thing to uniquely Haskell, and in fact it seems quite a bit easier to express without this extra complexity baggage of "getAny" (rather misleading, and annoying boilerplate imo) or the info-doc lookups you had to do for whether haskell's arbitrary definition of boolean classified it as a monoid or not. Again, the video is great, it's just that one small point you made 2x was just flatout wrong. JS is not limited in this respect. In fact if anything, JS isn't limited at all, besides things like macros like you get out of lisp, but I don't think haskell has anything close to it. its problems come from the lack of limitation. or maybe with haskell, if you're doing mathematical proofs, stuff like abstract properties of the operations and maybe operator overloads though i don't really know enough about haskell to say one way or another.