Тёмный
Well-Typed
Well-Typed
Well-Typed
Подписаться
We are Well-Typed, the Haskell Consultants. Since 2008, we assist companies all over the world in using the Haskell programming language, and we are contributors to GHC, Cabal, HLS, and other key parts of the Haskell ecosystem. We also offer training in Haskell.

On this channel, we regularly publish Haskell-related videos and tutorials.
06-11 Other Monads (Introduction to Haskell)
27:31
2 месяца назад
06-06 Counter (Introduction to Haskell)
17:58
2 месяца назад
05-02 The IO Type (Introduction to Haskell)
23:02
2 месяца назад
05-04 Bind (Introduction to Haskell)
24:19
2 месяца назад
05-03 Sequencing (Introduction to Haskell)
25:43
2 месяца назад
05-05 do Notation (Introduction to Haskell)
27:53
2 месяца назад
04-04 Overloading (Introduction to Haskell)
34:03
2 месяца назад
04-03 Union Types (Introduction to Haskell)
22:00
2 месяца назад
Комментарии
@larsbrunjes1798
@larsbrunjes1798 11 часов назад
You could have made Three and Grid instances of Applicative - then the initial grid would just be pure Empty, and it might have other advantages as well.
@wave_s6782
@wave_s6782 День назад
this was so nice! i’ve implemented this exact algorithm for tic-tac-toe in haskell but with a totally different (maybe less interesting) representation so it was really cool to see a different perspective. thanks
@tobzdaman619
@tobzdaman619 День назад
Thanks for this! :)
@JonesNoahT
@JonesNoahT 2 дня назад
Thank you
@BenHutchison
@BenHutchison 9 дней назад
Hi Andres, one aspect I'm curious about is *adoption* of the Num typeclasses. What proportion of Haskell libraries are written in terms of abstracted Num types, vs just using concrete types like Int, Double etc? Would you nominate any "exemplary" examples of Haskell libraries written in terms of abstracted Num classes, that demonstrate best practices applied?
@BenHutchison
@BenHutchison 9 дней назад
Great content! Your deep experience makes all the difference with this sort of topic..
@asdfghyter
@asdfghyter 13 дней назад
Right after watching this video I started watching a presentation (titled "polynomial interfaces") which mentioned how a UI in Elm is defined by an update function, an initial function and a view function, which happens to match the type signature of the Fold type from the foldl library (update=step and view=extract). I would probably not have noticed this connection if I didn't watch these two videos directly after each other. This makes me wonder if the Fold library could also coincidentally be useful for defining user interfaces, where a scan over the stream of events would be how the Fold is consumed. I guess this is also somewhat related to the concept of FRP which Elm was initially based on.
@asdfghyter
@asdfghyter 14 дней назад
A very minor question: I was looking through the source code for function generation and was wondering why this line: abstract (Prod p) d (x,y) = abstract (fmap (\q -> abstract q d y) p) d x wasn't instead defined as abstract (Prod p) d (x,y) = abstract (abstract p d x) d y which seems simpler and avoids traversing the data structure an extra time?
@noomade
@noomade 18 дней назад
Hi Andres, I am wondering if you have reconsidered extending your lectures into more intermediate Haskell? :)
@andresloeh
@andresloeh 13 дней назад
I'm definitely not ruling it out, but there are also no really concrete plans right now, so it might be a while until this happens. In the meantime, the Haskell Unfolder of course occasionally covers more advanced topics.
@byteeater7662
@byteeater7662 Месяц назад
A couple of questions have come to my mind near the end. Is there a strict counterpart to the Identity monad? Is there (or can be written) a monad transformer that makes any monad strict? Would this transformer commute with all possible transformers? In a universal way, i.e. with a pair of mutually inverse isomorphisms between their compositions in different orders?
@byteeater7662
@byteeater7662 Месяц назад
And what about the other direction: strict to lazy?
@well-typed
@well-typed 22 дня назад
Good questions! Yes, there is a strict counterpart to the identity monad, and we can indeed write monad transformers that make an underlying monad strict or lazy (in the values). It would probably not commute though; in particular, if you commute "MkStrict" and "MkLazy" transformers, I _think_ the one on top simply wins (but perhaps I've not thought this through enough!). I've added some examples to illustrate these points to the Unfolder repo; see github.com/well-typed/unfolder/blob/main/episode019-repeatedly/app/Aux.hs .
@JackSchpeck
@JackSchpeck Месяц назад
I really like that thinkInfo is now showing the source location! Productivity tip: Did you know, that when the terminal in vscode is showing reference to a line of code/src span (like app/Main.hs:32:28-38 at ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-NTW62s3mrXQ.html in the video), you can actually ctrl+click that reference to jump to the referenced code? A huge time-saver, when using things like ghcid or ghciwatch from vscode..
@Axman6
@Axman6 Месяц назад
This is probably an orthogonal issue, more related to performance, but I wanted to check: the solution here forces the results inside the function passed to modifyMVar_, which means that all accesses of the state need to wait for that evaluation to complete before the MVar is filled. If you were doing this in production, would you instead use modifyMVar, and then force the return value after the MVar has been written to, a la atomicModifyIORef’? The only downside I can see is that if the evaluation causes an exception to be thrown, that will have been written to the MVar and not caught by the (presumably) bracket call modifyMVar is implemented with. It seems like a beneficial thing to do if you can be sure the update function can never produce an exception.
@well-typed
@well-typed Месяц назад
It's an interesting question, not one that I had previously thought about. You are right, by forcing the result to WHNF you'd be holding on to the lock for longer than strictly necessary. If that really is a problem in a particular application, I could imagine a setup where you don't force the value to WHNF on update, but instead have a thread running that every so often pokes the value, effectively allowing a temporary (and limited) thunk build up. I've never worked on an application where this was necessary, but seems plausible.
@goncalorodrigues1964
@goncalorodrigues1964 Месяц назад
Being a pedant, but there is a valid function from a concrete type to a polymorphic type, namely `absurd: Void -> a`.
@andresloeh
@andresloeh Месяц назад
Good point! You are right, and perhaps I should rephrase this somewhat in a future version to acknowledge this special case. It doesn't change anything about the rest of the argument though.
@ahk2468
@ahk2468 Месяц назад
Really nice lectures. Thanks.
@TomSmeding
@TomSmeding Месяц назад
Cool stuff! Previously one could already get backtrace info on exceptions in profiling builds with +RTS -xc, but that was very noisy in practice because it prints a backtrace for _every_ exception - and it's often not even clear which of those exceptions is the actual problem. Having an easy way to get good backtraces for mostly only the exceptions that matter is definitely a boon for debugging!
@bmno.4565
@bmno.4565 Месяц назад
Good underrated language.
@i-am-the-slime
@i-am-the-slime Месяц назад
I only really know PureScript but wouldn't it be faster to use something like STArray and allocate a list with the same size at the start and walk over the original structure backwards and copy the cells over? Maybe there's some kind of memcpy that works backwards?
@andresloeh
@andresloeh Месяц назад
What you describe isn't going to be faster for a (linked) list. Already determining the length of the original list requires a full traversal of the list. Anything converting to a different data structure is going to be additional effort. Sure, if you are starting with arrays, then it's a different story. But for lists, the given accumulator-based algorithm is best. PureScript lists use the same approach, see github.com/purescript/purescript-lists/blob/v7.0.0/src/Data/List.purs#L365-L369
@lkbasgiohbasg
@lkbasgiohbasg Месяц назад
thanks for making this! is there some way to use this to get a backtrace from an operation that's in an infinite loop seemingly? I tried throwTo to that thread and catching higher up, but the ExceptionContext is empty
@well-typed
@well-typed Месяц назад
Good question. Unfortunately, I think the answer is no: first, throwTo has not been modified to collect backtraces, but even if it did (you could write your own version that did, I suppose), the backtraces that it would collect would point you to the throw, not where to the point in the code that was looping prior to being interrupted by the exception. Perhaps you could try running your code in gdb, interrupt it during the looping, and then use DWARF information (see www.well-typed.com/blog/2020/04/dwarf-1/ and follow-ups for some information) -- though perhaps this could be an episode on its own right :)
@lkbasgiohbasg
@lkbasgiohbasg Месяц назад
Thanks, ya I did try setting a breakpoint as close to the problem as I could, which worked; but I couldn't figure out how to step through reliably/efficiently since it's one Haskell thread we're interested in
@mybdretaemch3409
@mybdretaemch3409 Месяц назад
very detailed explanation!!
@danieldiaz6025
@danieldiaz6025 2 месяца назад
Great video. I was trying "annotateIO" in a program run with runhaskell/ghci and I didn't saw any annotations. They only appeared when running the compiled program. I suspect it's because the "losing annotations when re-throwing" effect described at 9:50.
@edskodevries
@edskodevries 2 месяца назад
Ah, yes, perhaps I should have mentioned that -- I also noticed that it doesn't work in ghci. I'm actually not entirely sure why! I suspect that it might simply be that the exception handler used by ghci has not been updated. Perhaps this should be a ghc ticket.
@johnwalker7422
@johnwalker7422 2 месяца назад
Thanks for another super interesting vid. Definitely upgrading my ghc version soon
@JesusCastello
@JesusCastello 2 месяца назад
Hello there! I ran some benchmarks using Criterion library: accumulator vs explicit strict acumulator vs regular foldl. It seems like when compiling with optimizations (-O1) the compiler optimizes the accumulators to be strict because all 3 versions produce the same benchmark results, in other words, they are equally as fast. The differences can only be appreaciated after disabling optimizations (-O0), then you can clearly see a perfromance differences with foldl actually being the slowest, followed by the lazy accumulator version. Maybe this is why foldl' (strict version) is not included in the default Prelude. Is there something else I'm missing or that I should know? And are you familiar In which situtations this optimization will not work automatically? Thanks! :)
@andresloeh
@andresloeh 2 месяца назад
Yes, the component in GHC which might "fix" the problem is called the strictness or demand analyzer, which by default is run when `-O1` or above is active. It is, however, fragile. It's good practice to (a) use `foldl'` instead of `foldl` explicitly, and (b) make accumulators strict explicitly. This is one of generally comparatively few situations where manually adjusting evaluation order is really helpful, and understanding the concepts involved sufficiently to be able to confidently do so saves you a lot of confusion later on. It's beyond the scope of an introduction course to go into more detail about strictness analysis being fragile. But just to provide one example, I think that e.g. defining `sum xs = foldl (+) 0 xs` and marking it `NOINLINE` with a compiler pragma will make it leak space with `-O1` again. It's great that the demand analyser exists. It can often enable further optimisations such as unboxing which make a big difference in practice. But in places where it changes a program from otherwise leaking space to not doing so, I would try not to rely on it.
@johnwalker7422
@johnwalker7422 2 месяца назад
We could also map the function into the first argument and forget about pure right? f <$> m a <*> m b ......
@andresloeh
@andresloeh 2 месяца назад
Yes, correct, `f <$> x` is generally supposed to be the same as `pure f <*> x`. I think the latter is slightly easier to understand: you "lift" the function into the effectful context and then apply all the argument of the function using <*>. With `<$>`, the first argument is slightly special, but yes, it works, and it saves some keystrokes.
@dr-Jonas-Birch
@dr-Jonas-Birch 2 месяца назад
Except for the Nottingham professor you are by far the best Hs resource on this platform, which makes it awkward that you have so few views. I am too a coding chan RU-vidr and since your content is so good, there must be that you're doing RU-vid wrong. Remember, the absolutely most important action is making sure your episodes publications have exactly the same interval. If you differ + or - just a couple of hours the algoritm gives you a penalty with regards to # of impressions. The more it differs the worse it gets. But if you publish the exact same interval, you will see incredible results just after 3-4 episodes. Take care, dr. Jonas Birch
@logauit
@logauit 2 месяца назад
Thank you for very clear explanation about stimulated side effect vs real side effect (only with IO monad) (7:30)
@tobzdaman619
@tobzdaman619 2 месяца назад
Didn't understand anything about type families up until today. They're still daunting but this video is very helpful and I'll be following along on the weekend. The boilerplate is a bit painful and you do have to work quite a ways backwards to figure out what's going on: but the power is apparent. Thank you very much! :D
@logauit
@logauit 2 месяца назад
Thank you so much for the course!
@designernasser
@designernasser 2 месяца назад
Thank you very much for this course. I think it's the best one on youtube, by far. Small note: I noticed that the videos 02-03 and 06-12 are missing from the playlist.
@well-typed
@well-typed 2 месяца назад
We're glad you like the course. Regarding 02-03 and 06-12, this is admittedly somewhat confusing, but these numbers are not for videos, but for assignments; see the course site at teaching.well-typed.com/intro/. It's only noticeable for these two, because the other assigments are all at the very end of their respective course parts.
@maxreuv
@maxreuv 2 месяца назад
Excellent intro course, thank you very much!
@pdr.
@pdr. 2 месяца назад
Thank you very much Andres and Well-Typed for making these publicly available!
@dalek6779
@dalek6779 2 месяца назад
Best Haskell tutorial series on RU-vid
@ribosomerocker
@ribosomerocker 2 месяца назад
Nice video! Can you edit it so that the keyboard noise isn't so loud? It's a little obnoxious...
@byteeater7662
@byteeater7662 2 месяца назад
Can there be a variant of Flatten that flattens [[True], [False, True]] to [True, False, True] and Just (Just (Just True)) to Just True (I think it's clear enough from these examples what generalization I've got in mind)?
@edskodevries
@edskodevries 2 месяца назад
Certainly! We demonstrated flattening Maybe to list just as a teaching device; flattening maybes in the manner that you describe would be quite natural. You will need to introduce a second type family IsMaybe alongside IsList. Nice exercise for the reader :)
@byteeater7662
@byteeater7662 2 месяца назад
I've envisioned a universal solution whereby no IsList, IsMaybe (perhaps IsTree and other similar variants too) are needed, just one generalizing all of them (subject to some constraint, likely Functor or Applicative). Maybe quantified constraints make it possible?
@edskodevries
@edskodevries 2 месяца назад
@@byteeater7662 That is a lot trickier to do. You could certainly provide an instance for (m a) , and then insist that (m) must be a monad (so that we can call join :: m (m (a) - > m a), but now you are ruling out any instances of the same shape (f a) -- unless you use overlapping instances again.
@dalek6779
@dalek6779 2 месяца назад
These tutorials are pure gold! Thank you very much for putting them together.
@AndreiGeorgescu-j9p
@AndreiGeorgescu-j9p 3 месяца назад
The constant mouth sounds makes me nauseous. You can't make videos like this with a laptop mic, no pop filter, no audio editing, and clearly without a cleared throat
@AndreiGeorgescu-j9p
@AndreiGeorgescu-j9p 3 месяца назад
The mouth sounds are unbearable
@logauit
@logauit 3 месяца назад
Thank you!. can i use this course as material to make the video about solving the assignments (A.hs, B.hs, C.hs)?
@well-typed
@well-typed 3 месяца назад
We might eventually publish additional materials, but right now, no solutions or additional assistance for the assignments are available in the free version of the course. We hope you find the course interesting and useful nevertheless!
@logauit
@logauit 3 месяца назад
@@well-typed sorry, my typo. my meant is can i make the video about solving your assignments?
@tophy9865
@tophy9865 3 месяца назад
You shorten (\y -> y >= 5) to (>= 5) but you can do the same with (\x -> x * x) with (^ 2)
@andresloeh
@andresloeh 3 месяца назад
Yes, you absolutely can. The reason I'm not putting emphasis on this here is that due to the overloaded type of (^) and me proposing -Wall, you will then get defaulting warnings, and overloading in general and defaulting in particular is a topic I'm only handling thoroughly in Part 4 of the course ...
@simpleshun
@simpleshun 3 месяца назад
Thank you for the uploads :)
@christophergenovese1018
@christophergenovese1018 3 месяца назад
Great stuff, as always. Thanks!
@logauit
@logauit 3 месяца назад
Thank you so much. This is really interesting topic! Esp Yin & Yang (!; ;)
@el_carbonara
@el_carbonara 3 месяца назад
I love how you can mechanically reason about the other half of the implementation and it just makes sense in the end
@well-typed
@well-typed 3 месяца назад
Yes! So satisfying :)
@pedrovasconcelos8260
@pedrovasconcelos8260 3 месяца назад
Great video on a very interesting topic! I liked that you start by showing that duality is used on other scientific and engineering disciplines and then proceed to discuss how it can inform the Haskell implementations. This is really drives the point that it is not just an elegant theoretical observation but rather a useful tool for problem solving. I have a single question regarding the first example of compression and decompression: in the final solution there is still some asymmetry between the two functions because decompression may fail with an error. Any thoughts on why this is?
@JackSchpeck
@JackSchpeck 3 месяца назад
I guess it's the asymmetry between encoding and decoding. Whereas you can probably encode any ByteString, it's unlikely that any random ByteString represents valid encoding of some data. This is a similar situation to when you encode Int to String, so you have total function `Int -> String`, but not every string can be decoded into Int, so `String -> Int` would be partial function.
@andresloeh
@andresloeh 3 месяца назад
@@JackSchpeck Yes, I think I agree with this reply. Perhaps if one could make the types more precise, one could prevent the error condition and make the symmetry even more complete.
@edskodevries
@edskodevries 3 месяца назад
Yeah, I've been wondering about this as well. @JackSchpeck 's reply is true of course, but it feels like we might be able to express that fact (decompression can fail, compression cannot) a bit more formally to make that duality more clear. Not totally obvious to me right now how though. If anyone has any suggestions, I'd love to hear them :)
@q_rsqrt5140
@q_rsqrt5140 3 месяца назад
Is it possible to automatically infer dual types?
@andresloeh
@andresloeh 3 месяца назад
I'm not sure what exactly you mean. There's some design flexibility in what one wants "dual" to mean in a particular setting. There are generally multiple concepts / instances of concepts involved, and only some of them have to be flipped. If the rules are sufficiently clear in a particular scenario, then one can implement a type family that computes the dual type from a given type. There are some instances where this is being done, e.g. in various experiments of encoding session types in Haskell.
@highSmoke
@highSmoke 3 месяца назад
Thank you for the lecture! But could you resize your cam smaller so that we can watch your screen in a bigger size?
@well-typed
@well-typed 3 месяца назад
Thanks for the suggestion. We'll consider this for a possible future re-recording or revision of the course, but for now, all videos of all parts already exist. At least at full resolution, the demo screen contents should always be ok to read.
@phantalicious
@phantalicious 3 месяца назад
What I'd really like to know is which Vim packages Andres is using to get such nice LSP integration 🙂
@andresloeh
@andresloeh 3 месяца назад
Anything in particular you want to know? Essentially just nvim-native lsp these days. Then telescope, nvim-cmp / cmp-nvim-lsp for completions, nvim-treesitter for highlighting, solarized as colour theme. The only slightly non-standard thing I'm aware of is that I have some custom lua code to make code lenses look a bit more to my liking.
@phantalicious
@phantalicious 3 месяца назад
@@andresloeh I notice your config shows instance signatures, and you seem to be able to run doctests from vim directly. That's probably codelenses doing their thing?
@andresloeh
@andresloeh 3 месяца назад
@@phantalicious Yes, HLS shows type signatures as code lenses also for declarations in instances. Running expressions from the editor is handled by the HLS "eval" plugin, which also adds the "Evaluate" / "Refresh" actions as code lenses.
@pedrovasconcelos8260
@pedrovasconcelos8260 3 месяца назад
Great video as usual. This technique is also used in QuickCheck to allow testing properties with variable number of parameters and different types.
@well-typed
@well-typed 3 месяца назад
Thanks! The situation with the `quickCheck` function is quite similar to the examples discussed in this episode, but still a bit different (which is the reason we didn't include it as an example). The `quickCheck` function isn't itself of variable arity, it is *parameterised* by a function of variable arity. The type is `Testable prop => prop -> IO ()`. The structure of the definition of the `Testable` instances is comparable to the classes discussed in the episode, with base cases for `Bool` and `Property` and then an instance for function types.
@aloussase
@aloussase 4 месяца назад
Very interesting, thank you very much!
@rubix4716
@rubix4716 4 месяца назад
Could you use this technique to create something like foldr or foldl', with two required args, where the first argment is a binary operation, the second is the base case, and the remaining N args are the "list to fold over"?
@well-typed
@well-typed 4 месяца назад
Yes, this can be done, although you can still run into problems with type ambiguities, so it's somewhat less user-friendly than a proper list-based fold. Such an example is included in the GitHub materials for this episode: github.com/well-typed/unfolder/blob/main/episode026-variable-arity-functions/VarArgs.hs#L132-L171
@impurepics
@impurepics 4 месяца назад
Fun, I really like this format! I wish there were more videos like this