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)
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 :) !
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.
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 ...```
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.
...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]`. ;)
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).
@@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.
@@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
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
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
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.
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.
i feel like tsoding in 2024 would not like this approach. it's good to know about monoids (for purposes of designing storage formats, algorithms, etc), but having monoid as an interface is a very dumb idea for several reasons: (1) same type can have many monoid operations, (2) it's more efficient to reduce an array of monoids at once instead of calling a binary operation on each pair of elements (3) it makes code much harder to understand bc locality of behavior is lost.
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.
I THINK FORBIDDING LOWERCASE CHARACTERS WOULD BE A GREAT RESTRICTION.
I AGREE WITH THAT STATEMENT
@@benzoaim TRUE
Now that's a epic haskell programming video
I have shown this video to my React codebase. It turned into Elm
I showed this video to my Elm project. It turned into PureScript
Nazarii Bardiuk My arch installation has actually gradually turned into NixOS. I’ve basically stopped using pacman now.
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
Whenever he says "what do I need for that?" The answer is always a function
It's Haskell, the answer to any question imaginable is a function.
"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)
same
I'm here to learn how to use monoids in my JavaScript.
JS can burn in hell
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 :)
!
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]
But that is monad. Not monoid. So it undermines the point of the video?
...but i agree it is the more "natural" code.
@@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.
@@dirrelito a monad is a monoid in the category of endofunctors. So it is still a monoid too
Breathtaking
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
More Haskell videos, they are so epic!
These are the best Haskell tutorials I've seen!
This is so bizarre he changed his tune so much
Why the hassle with importing stuff?
isForbidden c = any $ map ($ c) [isBraille, isEmoji]
Why the map?
isForbidden c = any ($ c) [isUpper, isDigit]
@@Kennnn264 why the function parameter? `isForbidden = or . () [isUpper, isAlpha] . pure`
isForbidden = or . sequenceA [isBraille, isEmoji]
I've been learning a lot from you! Your videos are great! Congrats! 👏👏👏
Cool, i get so excited when you post haskell content
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 ...```
*isForbidden c = any ($c) [ isEmoji, isBraille ]*
I don't like importing lesser known functions when the alternative is just as concise.
I used to Haskell but than I took a python to the knee.
I think the "m" in "mempty", "mappend", and "mconcat" stands for "M"onoid
the thing is why it is empty and append for an identity and a binary operation
That's a reference to the corresponding list operators, lists being the canonical monoid (also called the Free monoid)
The Crazy Train mix at the end makes this video even more epic :)
Which mix is it?
i like how there's an efficient solution 1:25 into the video but it's scrapped for epic haskell hubris
Lol, I read the title as referring to Monads and thought you might be aiming for something like "or . sequence predicates"
*the bot took a huge coredump*
Love it !
Your channel is too good to be true
Why not just map the argument to the predicates and use "or"?
I'd use:
isForbidden x = any $ map ($ x) predicates
Then you don’t get to use monoids. :P
@@asdfghyter I've used monoids in better ways before. You should get yourself the best, clearest solution not the one that uses monoids
Because, that won't be a "epic Haskell developer move" 😆😆
@@jadissa3841 Yeah exactly. I meant "any" not "or".
Respect... you really understad Monads and solid chunk of Haskell 🤯
„Max“ turns out to be a Join Semilattice, because it suffices idempotence.
This is beautiful. Maybe you'd want to mention "Haskell" in the video title, for people looking for tips in Haskell specifically.
For Haskell come to Tsoding.
did u just say that ascii art uses unicode characters?
Sadly this is no longer written in Haskell, he recently changed it to Go :(
I will someday became an Epic haskell developer
Name of the outro song?
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.
So this is how normal people feel when they see someone reason about code to implement
In JavaScript this is just
const predicates = [isNumeric, isLower];
const isForbidden = char => predicates.some(p => p(char));
...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]`.
;)
indeed that was a predicool video.
Outstanding!!!
why not
isForbidden x = and $ map ($ x) [the list of predicates]
Epic haskell programmer move.
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).
Your videos are gold
Really good explanation love it
Seems like you could continue this by explaining contravariants.
Instinctively I'd have just gone the applicative route, so something like this:
or $ "w" [isUpper, isLower]
@@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.
Also you can coerce predicates, so
isForbidden = getAny.mconcat predicates where predicates = coerce [isLower,isDigit]
So this mainly shows that monoids are a really complicated way of doing extremely primitive things.
Please also consider the reduction in lines of code and extensibility (here, the ability to add more conditions easily).
@@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
monoids it's good,but i don't know,how i can apply it in other situations.
Really good stuff!!! ♥️
Stunning!
I fucking love you! Thanks for this video!
str.includes() and you’re done. Why things have to be so complicated?
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
It's crazy to see that you've been doing crazy sh#t for so long 😂
mind blowed video!
that's what i call over engineering
Impressive! 👍🏻
Thx
foldMap all the things.
EPIC!
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
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.
very entertaining btw
damn, this is good
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.
Maybe Works
But... Why?
fuck I love haskell
Or with Applicative: `isForbidden = or . () [isUpper, isAlpha] . pure`
Are you a magician ?
i feel like tsoding in 2024 would not like this approach. it's good to know about monoids (for purposes of designing storage formats, algorithms, etc), but having monoid as an interface is a very dumb idea for several reasons: (1) same type can have many monoid operations, (2) it's more efficient to reduce an array of monoids at once instead of calling a binary operation on each pair of elements (3) it makes code much harder to understand bc locality of behavior is lost.
ya so epicc
Pog
I love haskell vert much....
Please...
Make video about chat bot on haskell...
Mech Maker watch the twitch live streams, he has a pre-planed schedule for them.
m'onoid
Epic
Could u explain Profunctor Optics in practice ? Please!!!!!!)
Javascript script-kiddies gonna hate this trick... 😅
🦄
much epic such uwu! that was really cool
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.
Works only with two predicates.
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.
you have no idea what youre talking about JS kiddo
An epic USELESS move!