There are two Haskell video courses available on LinkedIn Learning (formerly ‘Lynda DOT com’): [1] “Learning Haskell Programming” (~4 hrs long) [2] “Learning Haskell for Data Analysis” (~3.25hrs) That second one is some basic statistics, CSV, SQLite3 and Vizualizations work. Not sure what level you’re coming from, but if you’re somewhat of a beginner then should be enough to get you started on that front :)
I will probably talk about the differences in another video (stay tuned!) but here is a short explanation: "type" is creating an alias for an already existing type. A good example is the FilePath type in System.IO (type FilePath = String). A FilePath is just a String in this case. For type inference "newtype" is equivalent to "data" (with the limitation that only one constructor can be used when using "newtype"). There are some differences when it comes to evaluation (wiki.haskell.org/Newtype) but for type inference that's not important. In general you can replace every "newtype" with "data" and your program should still work fine. " ` " can be use to use constructors in an infix style. Suppose we have "data Test = A Int Int". We can construct a value by writing "A 1 2" or "1 `A` 2". They are equivalent. This also works for functions: "1 `elem` [1,2,3]". As we can see here, this infix style sometimes makes the code more readable but it is up to the programmer to make the decision what style should be preferred.
One note on your explanation of variables and functions. I assume (I don’t know it but I’m pretty sure) that there are actually only values and these values can be any object and functions are just objects too. Constructors are objects too, specifically functions (you often differentiate between those). I think that variables are in truth also functions, and when you use them, the function gets called. That’s where the laziness comes from, because every time you use a variable, it gets calculated again (about this part I’m the most unsure as it would be very inefficient, but you’re IO Video pretty clearly showed that. You set a variable to an IO Action and every time you used that variable, the action was performed, as if the variable was actually a function without any arguments). This may be harder to understand for some people than you’re explanation, but for me it’s much easier because it shows that everything is basically the same so you can do everything with everything
Close. Constructors can be used as functions, but they can also be used to destructure in pattern matching. Values (including those variables reference) don't get calculated repeatedly but once, and before then may be thunks; "thing-function" objects that know how to evaluate their value when needed without any further arguments. The IO action hw was performed each time it was fed into the runtime, but evaluated into its action form only once. AIUI, functions are incomplete closures still requiring more arguments before their result can be evaluated, and thunks are complete closures that have not yet been evaluated. For instance, if I declare "xs = [1..10]", xs is a thunk that knows how to build a list. If I then print (head xs), head requires the list constructor and print requires the contents of the item head extracted, so xs partially evaluates into 1:[2..10] where : is the constructor holding the 1 value and the [2..10] thunk. Performing an IO action always requires access to the environment, and there's in principle just the one of those, in the runtime that is performing main. The hw action was repeated not because the action changed but because the action was placed multiple times in the timeline.
But b = [b] makes totally sense! At least, that is what any mathematician is going to tell you. I'm talking about dedekind cut. A real number b is actually a set of real numbers (namely the rational numbers that make up the cut). So yeah, b = [b] SPOILER ALERT: I'm being half fascitious, becouse this definition of real numbers was never something I could swallow. But then again, I'm only an engineer. As long as calculus works, I don't care if real analysis and infinit set theory makes sense.
I know I'm late to reply here but for those reading this in the future, the situation here is as follows. Dedekind cuts define the real numbers by identifying every real number with a "cut" of rational numbers (i.e., a subset of rational numbers). One can think of this as identifying every real number with its decimal expansion (e.g., pi is identified with the set {3, 3.1, 3.14, ...}).
@@joshua-goldstein It seems that you are mixing up Dedekind cuts with Cauchy sequences. pi can be identified with the "cut" {x | x < pi} or the sequence (3, 3.1, 3.14, ...) depending on your construction. I encourage the reader to look up these two terms.
You have created very easy to follow videos. I am able to follow along as I am building my first Haskell app. Thank you.
Amazing tutorials. It would be much desirable if you would also make tutorials on data analysis in Haskell in the future :)
There are two Haskell video courses available on LinkedIn Learning (formerly ‘Lynda DOT com’):
[1] “Learning Haskell Programming” (~4 hrs long)
[2] “Learning Haskell for Data Analysis” (~3.25hrs)
That second one is some basic statistics, CSV, SQLite3 and Vizualizations work. Not sure what level you’re coming from, but if you’re somewhat of a beginner then should be enough to get you started on that front :)
What about 'type', 'newtype' and ` (in names)?
I will probably talk about the differences in another video (stay tuned!) but here is a short explanation:
"type" is creating an alias for an already existing type. A good example is the FilePath type in System.IO (type FilePath = String). A FilePath is just a String in this case.
For type inference "newtype" is equivalent to "data" (with the limitation that only one constructor can be used when using "newtype"). There are some differences when it comes to evaluation (wiki.haskell.org/Newtype) but for type inference that's not important. In general you can replace every "newtype" with "data" and your program should still work fine.
" ` " can be use to use constructors in an infix style. Suppose we have "data Test = A Int Int". We can construct a value by writing "A 1 2" or "1 `A` 2". They are equivalent. This also works for functions: "1 `elem` [1,2,3]". As we can see here, this infix style sometimes makes the code more readable but it is up to the programmer to make the decision what style should be preferred.
Can b be a list of bees?
Only if you allow infinite recursion inside lists.
b = [b]
Substitute b on the right side
b = [[b]]
Substitute b on the right side
b = [[[[b]]]]
etc.
b = 🐝 : [🐝]
One note on your explanation of variables and functions. I assume (I don’t know it but I’m pretty sure) that there are actually only values and these values can be any object and functions are just objects too. Constructors are objects too, specifically functions (you often differentiate between those). I think that variables are in truth also functions, and when you use them, the function gets called. That’s where the laziness comes from, because every time you use a variable, it gets calculated again (about this part I’m the most unsure as it would be very inefficient, but you’re IO Video pretty clearly showed that. You set a variable to an IO Action and every time you used that variable, the action was performed, as if the variable was actually a function without any arguments). This may be harder to understand for some people than you’re explanation, but for me it’s much easier because it shows that everything is basically the same so you can do everything with everything
Close. Constructors can be used as functions, but they can also be used to destructure in pattern matching. Values (including those variables reference) don't get calculated repeatedly but once, and before then may be thunks; "thing-function" objects that know how to evaluate their value when needed without any further arguments. The IO action hw was performed each time it was fed into the runtime, but evaluated into its action form only once. AIUI, functions are incomplete closures still requiring more arguments before their result can be evaluated, and thunks are complete closures that have not yet been evaluated.
For instance, if I declare "xs = [1..10]", xs is a thunk that knows how to build a list. If I then print (head xs), head requires the list constructor and print requires the contents of the item head extracted, so xs partially evaluates into 1:[2..10] where : is the constructor holding the 1 value and the [2..10] thunk.
Performing an IO action always requires access to the environment, and there's in principle just the one of those, in the runtime that is performing main. The hw action was repeated not because the action changed but because the action was placed multiple times in the timeline.
But b = [b] makes totally sense!
At least, that is what any mathematician is going to tell you. I'm talking about dedekind cut.
A real number b is actually a set of real numbers (namely the rational numbers that make up the cut).
So yeah, b = [b]
SPOILER ALERT: I'm being half fascitious, becouse this definition of real numbers was never something I could swallow. But then again, I'm only an engineer. As long as calculus works, I don't care if real analysis and infinit set theory makes sense.
I know I'm late to reply here but for those reading this in the future, the situation here is as follows. Dedekind cuts define the real numbers by identifying every real number with a "cut" of rational numbers (i.e., a subset of rational numbers). One can think of this as identifying every real number with its decimal expansion (e.g., pi is identified with the set {3, 3.1, 3.14, ...}).
@@joshua-goldstein It seems that you are mixing up Dedekind cuts with Cauchy sequences. pi can be identified with the "cut" {x | x < pi} or the sequence (3, 3.1, 3.14, ...) depending on your construction.
I encourage the reader to look up these two terms.