Well-Typed
Well-Typed
  • 93
  • 63 936
06-13 Conclusions of the Course (Introduction to Haskell)
We have reached the end of the course. In this short video, I reflect on what we have achieved now and provide some pointers on how you can continue your Haskell journey.
Course site with self-test questions: teaching.well-typed.com/intro/monads.html#video-613-conclusions-of-the-course
มุมมอง: 246

วีดีโอ

06-11 Other Monads (Introduction to Haskell)
มุมมอง 2264 หลายเดือนก่อน
The monad interface is so compelling because there are, in fact, lots of types that can be made an instance. However, many types are in fact variants or combinations of the cases we have now already discussed. We discuss a few such variants. We also mention lists as an example of a Monad instance that has a rather different flavour from the examples we have seen so far. Course site with self-te...
06-10 Generalising from Counter to State (Introduction to Haskell)
มุมมอง 1604 หลายเดือนก่อน
While Counter is a type that we have defined for the purposes of our examples, a more general form of Counter actually exists in the libraries, called State. We show how we can access the general functionality. Course site with self-test questions: teaching.well-typed.com/intro/monads.html#video-610-generalising-from-counter-to-state
06-09 Using the Applicative Interface (Introduction to Haskell)
มุมมอง 1754 หลายเดือนก่อน
The definition of labelTreeAux does not actually require the full power of “bind”. None of the counter-maintaining computations depend on the results of previous computations. The shape of the tree alone determines the sequence of computations we want to build. This means that we do not have to use “bind” or do notation in the definition. We can use operations provided by the Applicative class....
06-08 Functor, Applicative, Monad (Introduction to Haskell)
มุมมอง 1844 หลายเดือนก่อน
In order to actually make Counter and instance of the Monad class, we have to navigate the Haskell class hierarchy and also provide instance declarations for the Applicative and Functor classes. Fortunately, if we have “bind” and “return” both available, all other methods we have to define can be mechanically completed in terms of just these two basic ingredients. Course site with self-test que...
06-07 Monad Operations for Counter (Introduction to Haskell)
มุมมอง 1544 หลายเดือนก่อน
We derive a “bind” and a “return” operation for counters and see how we can rewrite and now drastically simplify the definition of labelTreeAux by applying these functions, hiding the threading of the counter completely and thereby removing the potential for making mistakes. Course site with self-test questions: teaching.well-typed.com/intro/monads.html#video-67-monad-operations-for-counter
06-06 Counter (Introduction to Haskell)
มุมมอง 1664 หลายเดือนก่อน
As an intermediate step before we identify a recurring pattern in the tree-labelling example, we introduce a Counter type to represent computations that maintain an Int-typed counter in order to come up with a result. We refactor the definition to make use of this Counter type and additionally introduce a helper function stepCounter to make the overall shape of the code more regular. Course sit...
06-05 Recap: Labelling Trees (Introduction to Haskell)
มุมมอง 1424 หลายเดือนก่อน
As another and entirely independent example, we consider a task from Assignments B, that of labelling all the nodes in a binary tree with unique labels. As a preparation, we first solve this task in classic pattern-matching style. We need to introduce an auxiliary function that has an extra integer as an input (representing the label from which we want to start labelling the current tree), and ...
06-04 More Functions for Free (Introduction to Haskell)
มุมมอง 1584 หลายเดือนก่อน
06-04 More Functions for Free (Introduction to Haskell)
06-03 The Monad Class (Introduction to Haskell)
มุมมอง 1574 หลายเดือนก่อน
06-03 The Monad Class (Introduction to Haskell)
06-02 Sequencing Possibly Failing Computations (Introduction to Haskell)
มุมมอง 1594 หลายเดือนก่อน
06-02 Sequencing Possibly Failing Computations (Introduction to Haskell)
06-01 Preparation: Finite Maps (Introduction to Haskell)
มุมมอง 2324 หลายเดือนก่อน
06-01 Preparation: Finite Maps (Introduction to Haskell)
05-08 File Operations and Exceptions (Introduction to Haskell)
มุมมอง 2384 หลายเดือนก่อน
05-08 File Operations and Exceptions (Introduction to Haskell)
05-06 Mapping and Filtering with IO Actions (Introduction to Haskell)
มุมมอง 2334 หลายเดือนก่อน
05-06 Mapping and Filtering with IO Actions (Introduction to Haskell)
05-02 The IO Type (Introduction to Haskell)
มุมมอง 2884 หลายเดือนก่อน
05-02 The IO Type (Introduction to Haskell)
05-10 unsafePerformIO (Introduction to Haskell)
มุมมอง 1634 หลายเดือนก่อน
05-10 unsafePerformIO (Introduction to Haskell)
05-01 Why Explicit Effects? (Introduction to Haskell)
มุมมอง 4084 หลายเดือนก่อน
05-01 Why Explicit Effects? (Introduction to Haskell)
05-04 Bind (Introduction to Haskell)
มุมมอง 2384 หลายเดือนก่อน
05-04 Bind (Introduction to Haskell)
05-09 Interaction Trees (Introduction to Haskell)
มุมมอง 3674 หลายเดือนก่อน
05-09 Interaction Trees (Introduction to Haskell)
05-03 Sequencing (Introduction to Haskell)
มุมมอง 2544 หลายเดือนก่อน
05-03 Sequencing (Introduction to Haskell)
05-05 do Notation (Introduction to Haskell)
มุมมอง 1804 หลายเดือนก่อน
05-05 do Notation (Introduction to Haskell)
05-07 Excursion: Working with Packages (Introduction to Haskell)
มุมมอง 2134 หลายเดือนก่อน
05-07 Excursion: Working with Packages (Introduction to Haskell)
04-04 Overloading (Introduction to Haskell)
มุมมอง 2794 หลายเดือนก่อน
04-04 Overloading (Introduction to Haskell)
04-02 Parametricity: The map Function as an Example (Introduction to Haskell)
มุมมอง 1584 หลายเดือนก่อน
04-02 Parametricity: The map Function as an Example (Introduction to Haskell)
04-03 Union Types (Introduction to Haskell)
มุมมอง 1524 หลายเดือนก่อน
04-03 Union Types (Introduction to Haskell)
04-06 Type Class Overview: Show, Read (Introduction to Haskell)
มุมมอง 1554 หลายเดือนก่อน
04-06 Type Class Overview: Show, Read (Introduction to Haskell)
04-05 Type Class Overview: Eq, Ord, Enum, Bounded (Introduction to Haskell)
มุมมอง 2004 หลายเดือนก่อน
04-05 Type Class Overview: Eq, Ord, Enum, Bounded (Introduction to Haskell)
04-08 Type Class Overview: Functor, Foldable (Introduction to Haskell)
มุมมอง 1804 หลายเดือนก่อน
04-08 Type Class Overview: Functor, Foldable (Introduction to Haskell)
04-10 The Monomorphism Restriction and Defaulting (Introduction to Haskell)
มุมมอง 1834 หลายเดือนก่อน
04-10 The Monomorphism Restriction and Defaulting (Introduction to Haskell)
04-09 Classes for Numbers (Introduction to Haskell)
มุมมอง 1344 หลายเดือนก่อน
04-09 Classes for Numbers (Introduction to Haskell)

ความคิดเห็น

  • @larsbrunjes1798
    @larsbrunjes1798 11 ชั่วโมงที่ผ่านมา

    Why don't you use tabulate to define positions, so you don't need that Applicative instance? poaitiona grid = tabulate $ \i -> (index grid i, setter i grid)

    • @andresloeh
      @andresloeh 3 ชั่วโมงที่ผ่านมา

      Yes, that would have been simpler.

  • @friedrichdergroe9664
    @friedrichdergroe9664 22 ชั่วโมงที่ผ่านมา

    I have been watching all your Haskell videos, the advanced ones as well as the introductory ones. They have all been an immense help. I am working on a major ML project right now that I am writing in Haskell, and I want to leverage the power of Haskell as much as I can. Please keep up the good work. Sad you don't get a lot of views, but it is always about quality, not quantity.

  • @friedrichdergroe9664
    @friedrichdergroe9664 วันที่ผ่านมา

    Love the Saturn V rocket!

  • @DrBartosz
    @DrBartosz วันที่ผ่านมา

    Cool to see Representable used on a non-trivial example. One way of thinking about Representable is that the index type is like a logarithm of your data type (if you visualize the function type i->a as an exponential a^i). Three and Grid are both product types, and you can always take a logarithm of a product--you get a sum. As you mentioned, list is not representable because it's a sum type: List a = 1 + a * List a. An infinite list, on the other hand, is an infinite product, so it's representable.

  • @pedrovasconcelos8260
    @pedrovasconcelos8260 3 วันที่ผ่านมา

    Great video as usual! I have a question regarding performance overhead: can GHC optimize away the Distributive/Representable for Grid and obtain something similar to the initial hand written code? I initially thought yes because all the functions involved are non-recursive, but then realized that the indices IxThree exist at run-time representations and GHC will not be clever enought to optimize it way.

  • @Bx99875
    @Bx99875 12 วันที่ผ่านมา

    1:49 - Me learning Haskell

  • @Bx99875
    @Bx99875 13 วันที่ผ่านมา

    If any audio suggestion, you can use a quieter keyboard for video

  • @Bx99875
    @Bx99875 13 วันที่ผ่านมา

    Great video Andres!

  • @Bx99875
    @Bx99875 16 วันที่ผ่านมา

    Thank you Andres! Following you since the haskell courses for IOHK.

  • @Timjstewart
    @Timjstewart 19 วันที่ผ่านมา

    Thank you for such a clear, systematic, and elegant way of building up instances!

  • @petre-gx1oh
    @petre-gx1oh 19 วันที่ผ่านมา

    Indeed, very nice. I'm watching these introductory ones just in preparation for later subjects but I still enjoy them. Someone mentions sound issues, and while those might be annoying for the respective person, I found the lectures easy to listen to and clear, even while playing 1.75x.

  • @Timjstewart
    @Timjstewart 21 วันที่ผ่านมา

    Thanks for this great demonstration!

  • @georgH
    @georgH 22 วันที่ผ่านมา

    It was thanks to a video explaining monads through the operator <=< that I understood them (maybe from Bartosz Milewski??)

    • @edskodevries
      @edskodevries 20 วันที่ผ่านมา

      That sounds entirely plausible! Categorically what we are talking about here (a -> m b) are known as kleisli arrows, and <=< is composition in the kleisli category. So Bartosz, being all about category theory, taking <=< as primitive makes total sense :)

  • @masoom-theproudvegan7212
    @masoom-theproudvegan7212 22 วันที่ผ่านมา

    thank youu for amazing insightful content i love that you take time to explain thought process like a real programmer

  • @masoom-theproudvegan7212
    @masoom-theproudvegan7212 22 วันที่ผ่านมา

    hey just dropping and i am sorry that i forgot to comment in previous videos but your channel is just absolute treasure i learnt a lot of haskell and how to think in haskell way from you please keep going , also make videos on some hot topics like How rust programmer can pickup haskell how c programmers can benefit etc .... that will help the channel to grow and also a new perpective of thinking to many of us , thank you so much

  • @SuperZsomi
    @SuperZsomi 23 วันที่ผ่านมา

    Thanks for the example. Do you have more examples about the monads like Timed ?

    • @edskodevries
      @edskodevries 19 วันที่ผ่านมา

      What kinds of examples are you looking for? Do you mean monads that don't satisfy the laws? If so, then the episode on laws gives another example at th-cam.com/users/liveV7673JaWXaA?si=NLitoTJFLTK-j5eC&t=1475 although as we argue there, in that case it's more acceptable than the Timed example we discuss here. We discuss another such near-miss at the end of the eposide at th-cam.com/users/liveV7673JaWXaA?si=cMiMaO_rw4p7gPwf&t=1848 .

  • @dr-Jonas-Birch
    @dr-Jonas-Birch 25 วันที่ผ่านมา

    Great content as always. Your channel deserves to be bigger. Please heed my advice: remove access to the older livestreams and then you upload the recordings instead, as regular videos. Upload one episode every 4th day on the spot. You will be Amazed of the views and subscriber count after a couple of weeks. You can use the timer feature so it's not much work. I got from 0 subs to 20k in ten months. JB

  • @pedrovasconcelos8260
    @pedrovasconcelos8260 25 วันที่ผ่านมา

    Great video! I like the choice if using composition rather than bind and will try it whenever I teach monads again. Also: kudos for showing the importance of laws as a way of enabling refactorings. I'd just like to add that laws are also useful for reasoning about functions that operate over general monads e.g. sequence or foldM.

    • @well-typed
      @well-typed 24 วันที่ผ่านมา

      Agreed!

  • @lucastourinho6370
    @lucastourinho6370 25 วันที่ผ่านมา

    Great ep! Is it possible to provide a more sensible instance for Monad in the Timed example? Link to the 'Laws' episode mentioned @ 28:48 - th-cam.com/video/V7673JaWXaA/w-d-xo.html

    • @edskodevries
      @edskodevries 24 วันที่ผ่านมา

      Maybe the easiest way to do it would be to impose the timeout in the `MonadIO` instance, so that every time you `liftIO` an operation, you implicitly impose the timeout.

    • @well-typed
      @well-typed 24 วันที่ผ่านมา

      Thanks for the link, have now added it to the video description also :)

  • @christianschafer3724
    @christianschafer3724 25 วันที่ผ่านมา

    Great way to introduce the Monad. I never thought about it this way. Saying it is composition of two programs also makes a lot of sense and avoids the common Monad container analogy.

  • @MP3-b5w
    @MP3-b5w 25 วันที่ผ่านมา

    Thank you for the great video! It was very helpful.

  • @samuraijosh1595
    @samuraijosh1595 25 วันที่ผ่านมา

    No i don't

  • @ripwolfe
    @ripwolfe หลายเดือนก่อน

    These videos have been fantastic so far, but this one has been particularly helpful. I've read a lot about 'return' but this really solidified my understanding.

  • @tobzdaman619
    @tobzdaman619 หลายเดือนก่อน

    Ah, I finally understand how IOError and catching errors works! Thanks :) From the looks of it, you could define several different error handler functions that handle different errors and use that in the (IOError -> IO a) part.

  • @Bratjuuc
    @Bratjuuc หลายเดือนก่อน

    10:00 I wish there was a notion of a lens in Haskell...

    • @well-typed
      @well-typed หลายเดือนก่อน

      Since this was an episode targeted at beginners, we didn't want to have to introduce lenses in this episode. Bu you are right of course, this is precisely a lens, and we could have used a library such as hackage.haskell.org/package/optics to make that a bit more explicit.

  • @christophervalerio5964
    @christophervalerio5964 หลายเดือนก่อน

    I wonder how Andres runs the comments on repl as comments on vim

    • @andresloeh
      @andresloeh หลายเดือนก่อน

      Not sure if I understand your question, because I'm not doing it in this episode, or am I? If you mean the evaluation of `>>>`-prefixed comment lines, then it's the eval plugin of HLS, which you automatically get if you use HLS in your editor.

    • @christophervalerio5964
      @christophervalerio5964 หลายเดือนก่อน

      @@andresloeh yeah, sorry this is not in this episode. But yeah that's what I wanted to use. I use neovim as well so I will look into documentation for that eval plugin. Thanks!!!

    • @andresloeh
      @andresloeh หลายเดือนก่อน

      @@christophervalerio5964 Depending on what you're using for general LSP support in neovim, you might have to explicitly enable code lenses.

  • @lucastourinho6370
    @lucastourinho6370 หลายเดือนก่อน

    very very good

  • @ŊŊŊ-d1q
    @ŊŊŊ-d1q หลายเดือนก่อน

    Top tier haskell content, absolutely love it. Keep it up!

  • @hellwolf4liberty
    @hellwolf4liberty หลายเดือนก่อน

    Thanks for the new video. Some of you might also like to checkout the "Haskell Tiny Game Jam 2023", which has also a few board games written in under 10 lines :)

  • @محمدرواس-ص9غ
    @محمدرواس-ص9غ หลายเดือนก่อน

    Thanks for your effective , great content and your straight forward explination. Keep up the brilliant working Mr , haskell is awesome

  • @kellybrower7634
    @kellybrower7634 หลายเดือนก่อน

    @7:02 how did you get "forall s a" to populate in the right spot on the lines below with one motion?

    • @andresloeh
      @andresloeh หลายเดือนก่อน

      "Visual block mode" in Vim, basically selecting a rectangular region at the beginning of all the lines, then insert at the start of every line in the block with `I`, see e.g. neovim.io/doc/user/visual.html#v_b_I

    • @well-typed
      @well-typed หลายเดือนก่อน

      Using "Visual-block Insert" in (Neo)Vim, see neovim.io/doc/user/visual.html#v_b_I

  • @larsbrunjes1798
    @larsbrunjes1798 หลายเดือนก่อน

    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.

    • @andresloeh
      @andresloeh หลายเดือนก่อน

      Yes, absolutely, it would definitely be useful. There are many interesting class instances that `Grid` permits. I had considered talking about Applicative and even about things such as Distributive and Representable, but I eventually decided it would be too much of a distraction and just kept the (self-defined) lenses in.

  • @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 หลายเดือนก่อน

    Thank you

  • @BenHutchison
    @BenHutchison 2 หลายเดือนก่อน

    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 2 หลายเดือนก่อน

    Great content! Your deep experience makes all the difference with this sort of topic..

  • @asdfghyter
    @asdfghyter 2 หลายเดือนก่อน

    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 2 หลายเดือนก่อน

    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?

  • @byteeater7662
    @byteeater7662 2 หลายเดือนก่อน

    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 2 หลายเดือนก่อน

      And what about the other direction: strict to lazy?

    • @well-typed
      @well-typed 2 หลายเดือนก่อน

      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 2 หลายเดือนก่อน

    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 th-cam.com/video/NTW62s3mrXQ/w-d-xo.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 2 หลายเดือนก่อน

    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 2 หลายเดือนก่อน

      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 3 หลายเดือนก่อน

    Being a pedant, but there is a valid function from a concrete type to a polymorphic type, namely `absurd: Void -> a`.

    • @andresloeh
      @andresloeh 3 หลายเดือนก่อน

      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 3 หลายเดือนก่อน

    Really nice lectures. Thanks.

  • @TomSmeding
    @TomSmeding 3 หลายเดือนก่อน

    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 3 หลายเดือนก่อน

    Good underrated language.

  • @i-am-the-slime
    @i-am-the-slime 3 หลายเดือนก่อน

    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 3 หลายเดือนก่อน

      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 3 หลายเดือนก่อน

    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 3 หลายเดือนก่อน

      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 3 หลายเดือนก่อน

      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 3 หลายเดือนก่อน

    very detailed explanation!!

  • @danieldiaz6025
    @danieldiaz6025 3 หลายเดือนก่อน

    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 3 หลายเดือนก่อน

      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.