1. 5

    Coming from the DM, I had a hard time finding the gofundme link. Just in case others are as dense as I am, here it is: https://www.gofundme.com/lobsters-emoji-adoption

    1. 2

      It took me a moment to realize the link is the story link for this story. I’m just not used to seeing the announce tag on a link!

      1. 2

        Or on a primarily text post!

    1. 5

      @home: Preparing to release version 0.12 of Dramatiq. I want to improve some parts of the docs and some of the supporting material. I may even do a little intro screencast.

      EDIT: I ended up recording that screencast 🎉

      1. 2

        nice job on the recording, I have tried a few in the past and was never satisfied with the result. What recording software/hardware do you use?

        1. 3

          Thanks! I used ScreenFlow to record the video and Moom to size up the windows beforehand to 1280x720pt (2560x1440px) and many, many takes. When exporting the video I chose to do it at 1440p to avoid scaling artefacts. For the audio I used my apple earbuds in a fairly tall, echoey room and it shows :D

      1. 3

        @home: working on a Django app/integration for Dramatiq. It’ll have a run command and task module auto-discovery built-in, as well as an optional admin interface to manage tasks.

        @work: pairing w/ some people to improve our local dev. story.

        1. 1

          What exactly do you mean by “local dev story”?

        1. 1

          If I understand the docs, it seems like it’s on me, the user, to deploy and manage the code the workers run. It would be great (and a differentiating feature) if dramatiq could handle this – e.g., if the workers were dumb and ran code distributed by the submitter. Deploying code to dozens or hundreds of workers is always what makes these things painful to use.

          1. 1

            IIRC Cloud Haskell works in the way you propose. While interesting, I don’t think it’s a model I’d adopt for a dynamic language. At least not for your average SaaS workload!

          1. 1

            Dramatiq is licensed under the AGPL

            Now I have three options:

            • Make the codebase at work opensource (lol)
            • Violate AGPL on purpose
            • Use any of the other Redis-based task queues

            I really don’t get what the author is trying to achieve with choosing a license like this.

            1. 13

              You could also buy a license.

              1. 1

                Ah, that makes sense. I didn’t see that, shame on me.

              2. 4

                I think commercial backing of some sort or another is the only way we can sustainably develop open source software long term and dual licensing seemed like the lowest friction way to get started. I’ll have to highlight that fact a little better in the docs! :D

                1. 2

                  You are right of course. Other message frameworks like sidekiq seem to do alright: https://github.com/mperham/sidekiq

                  The challenge here is that Celery is in pretty great shape for a free solution. On the other hand Python’s support for high concurrency is changing rapidly so who knows maybe there’s room for a new player in this market.

                  1. 2

                    I’ve never met anyone IRL who’s worked with Celery and didn’t run into problems, so there’s definitely room for improvement in this area.

                    1. 2

                      It works like a charm with RabbitMQ as a backend. The rest is pretty experimental and breaks, especially at high volume. (I’ve been using Celery for >5 years)

                      1. 4

                        I’ve been using Celery professionally for about 3 years and dramatiq tries to solve many of the issues I’ve encountered using it. Some stuff that immediately springs to mind:

                        • Celery doesn’t support task prioritization. You have to deploy multiple sets of workers in order to prioritize queues.
                        • Celery has poor support for delayed tasks. Delayed tasks go on the same queue that normal tasks go on and they’re simply pulled into worker memory until they can be executed. This makes it hard to autoscale workers by queue size.
                        • Celery acks tasks as soon as they’re pulled by a worker by default. This is easy to change, but a bad default. Dramatiq doesn’t let you change this: tasks are only ever acked when they’re done processing.
                        • Celery tasks are not retried on error by default.
                        • Celery’s not well suited for integration testing. You’re expected to unit test tasks and to turn eager evaluation on for integration tests, but even then task exceptions will be swallowed by default. Dramatiq provides an in-memory stub broker specifically for this use case.
                        • The source code is spread across 3 different projects (celery, billiard and kombu) and it’s impenetrable. Its usage of runtime stack frame manipulation leads to heisenbugs.
                        • It’s easy for some of its more advanced “canvas” features to drop tasks.

                        All of the above are things that are first-class in dramatiq and there are definitely other things I’m not thinking of right now. That’s not to say that celery is bad, but I think we can do better and that’s why I made dramatiq. :D

                        1. 1

                          Considering your experience, I was wondering what’s your take on rq? (others who used it, are obviously welcomed to chime in too)

                          1. 1

                            I don’t have much experience with RQ since it is Redis-only and I’ve generally preferred to use RabbitMQ as a message broker. However, a few things that seem like disadvantages to me with RQ are:

                            • Messages are pickled so it’s strictly limited to Python and pickled messages are potentially exploitable. This also means you may sometimes send bigger messages than you intended over the network purely by accident.
                            • Queue prioritisation is handled like it is in Celery: you have to spawn different sets of workers.
                            • It forks for every job, so it’s slightly slower and forks that are killed b/c they’ve surpassed their time limits can leak DB connections if you’re not careful. I understand this may be swappable behaviour, however.
                            • Similar to Celery, there isn’t a good integration testing story for RQ.

                            Because I’ve criticised both Celery and RQ at this point, I feel it’s important that I mention a couple areas where they’re both currently better than dramatiq:

                            • the obvious one: it’s newer than either of those and is less likely to be familiar to users. The extension ecosystem for dramatiq is nonexistent (though I will be releasing integration packages for Django and Flask soon!)
                            • dramatiq doesn’t store task results and doesn’t offer a way to retrieve them. Adding that sort of functionality is trivial using middleware, but it’s not there ootb so if you absolutely need something like that and you don’t care about the things I have mentioned so far then you should look at Celery or RQ instead.
                            1. 1

                              Thank you for taking the time to post this!

                              There are two other areas that bother me personally:

                              • Python 3 only. While I would love to switch to Python 3, still need to maintain a large project in Python 2.
                              • The AGPL license. The above project is open source too, but I want to keep it BSD licensed to stay “friendly” towards potential users. Ironically, for a commercial project I would worry less about your license of choice, as I wouldn’t mind buying the commercial license when needed.

                              I share @jscn’s sentiment about Celery. I I was wondering if RQ, despite the above disadvantages might be more stable. At least their codebase should easier to grok (single repo)…

                              1. 1

                                Python 3 only. While I would love to switch to Python 3, still need to maintain a large project in Python 2.

                                I’m considering adding Python 2 support, but it’s a hard thing to balance what with 2.x getting EOL’d in a little less than 2 and a half years.

                                The AGPL license. The above project is open source too, but I want to keep it BSD licensed to stay “friendly” towards potential users. Ironically, for a commercial project I would worry less about your license of choice, as I wouldn’t mind buying the commercial license when needed.

                                Understandable.

                          2. 1

                            Sure, that’s true. Did you ever look at https://github.com/RichardKnop/machinery that project is still really early. Probably much easier to compete with.

                    2. 1

                      beanstalkd, NSQ, resque, celery, huey, … — pretty much everything in this space is non-GPL. So “use any other queue thing” will definitely be a very popular option :)

                      1. 5

                        So “use any other queue thing” will definitely be a very popular option :)

                        That’s perfectly fine! I just want those people that get value out of my work to contribute back in some way. If someone makes a cost-benefit analysis and decides that they’d rather use celery over dramatiq because they prefer the cheaper option (although it’s worth mentioning that I give out free comm. licenses for one year per company) then that’s their prerogative. I’ll still be around a year later when they realise their mistake ;).

                    3. 2

                      Trying to achieve you not using this at work? That’s usually what I’m going for when I choose AGPL

                    1. 3

                      This is cool, congrats! I’m going to play around with using it as a broker in dramatiq this weekend :D. A couple questions:

                      From what I can tell, a lot of things Faktory does overlap with things RabbitMQ provides ootb. How are you planning to differentiate long term?

                      Is the protocol documented anywhere? I haven’t been able to find anything. The best I could find was this file in the Ruby client.

                      1. 1

                        The wiki has some protocol docs. Right now, the wiki + faktory_worker_{go,ruby} are the things to look into.

                      1. 1

                        I’m been working on a Django app for Dramatiq and making small improvements to it in the process.

                        1. 5

                          I might be the only person in this thread who likes the new keyboard. I use a 2017 15” Macbook Pro with Touchbar for work and find the keyboard easier to type on than my 2015 13” Macbook Pro. I like the reduced travel distance and what I perceive as a louder click when typing.

                          The thing that changed my life, however, is setting CAPS LOCK to be ESC. I’ve done it across all of my computers now and would not have done so without Apple giving me a nudge when removing the physical ESC key on the Touchbar Macs. I don’t miss CAPS LOCK at all and the travel distance to ESC is so much more pleasing.

                          I do have problems with my hand sometimes brushing the touchpad if I’ve not positioned my wrists correctly. That’s a little aggravating but I’m largely over it now in the ~4 months I’ve been using this machine. Turns out I never really used the media keys much except for volume and pause/play so I don’t mind the touchbar and the extra info it can provide in many modern apps I use (e.g. Chrome, Outlook).

                          To each their own?

                          1. 4

                            I can totally see switching caps lock to be esc on the touch bar model. However, people who use the CTRL key a lot, like people running Windows or Linux or spend their day inside the terminal in macOS, might find it useful to swap CTRL and Caps Lock. Vim users might then want to start using CTRL+C instead of Esc to enter normal mode.

                            Especially people on MacBooks or Lenovos where the Fn and CTRL keys are all wrong should consider swapping the buttons if they ever use CTRL for anything.

                            1. 6

                              Set caps lock to BOTH Ctrl and Esc!

                              X11: xcape (like this)
                              Windows: AutoHotkey (like this)
                              macOS: karabiner-elements

                              1. 1

                                Is there a High Sierra work around?

                                1. 3

                                  I haven’t tried it (I’m still on Sierra) so can’t confirm, but the Karabiner Elements repo suggests it works on High Sierra. Karabiner Elements still has far fewer features than Karabiner though.

                                  1. 2

                                    There wasn’t, the last time I checked.

                                    1. 2

                                      A shame. I’m still on 10.11 and I won’t upgrade because my workflow depends on karabiner.

                                2. 3

                                  Just a warning for potential users of this setup: ^C and Esc aren’t exactly the same in vim. A major difference is entering text [count] amount of times (like 3i or 4A): hitting ^C to enter normal mode will only insert the new text once.

                                  1. 2

                                    That’s true. My .vimrc has the following lines to make ^C act as Esc in normal and insert mode:

                                    nmap <C-c> <ESC>
                                    imap <C-c> <ESC>
                                    
                                    1. 3

                                      You could use C-[ instead. It’ll work everywhere without any mappings and is equivalent to ESC.

                                3. 1

                                  Yeah, I concur. I like the new keyboard, even coming from a cherry MX green keeb on my desktop.

                                1. 6

                                  I’ve been using fish for a couple of years now and I love it not only because of its batteries-included nature (I have a few custom functions I wrote, but otherwise I’ve been basically running vanilla fish from the beginning), but also because of how damn fast it is while providing all of these great features out of the box. For bash utility compat. there’s bass but I’ve never really had to use it. virtualfish is a fantastic replacement for virtualenvwrapper.

                                  1. 7

                                    I do not like this electron fad.

                                    1. 7

                                      I had to make certain I had at least 16gb of RAM in all my future computers because of Electron/node-webkit-app-whatever. As it is, I’m using 12 gb just sitting here, no compiles.

                                      Culprits according to gnome-system-monitor sorted by memory? Chrome, Chrome, Chrome, Spotify, Chrome, Slack, Slack, Chrome, Chrome, Slack, Slack, Chrome, Chrome, Chrome (on and on for a whole page)

                                      1. 3

                                        I hope it’s just a fad and that it dies off as soon as possible, but I think it’s unlikely that it will.

                                        1. 1

                                          It’s not going to die off. Javscript is eating the world, one chunk at a time.

                                          I guess this means I should get off my butt and actually learn the silly language sometime soon. I’ve been doubling down on my Python mastery :)

                                        2. 3

                                          I’m generally in agreement, but this is one of the cases in which Electron is perfectly reasonable… The project is a web browser after all, so the weight of Chromium is not going to waste.

                                        1. -2

                                          Python is dead

                                          Update: Why the downvote? It’s quite obvious to me that Python is not a language you can trust going forward and there are plenty of good alternatives out there (Go, Swift, Nim, Elixir, etc). Why they have destroyed a perfectly nice language is beyond me.

                                          1. 5

                                            Saying “X is dead” in the technology world is pretty much a no-op. It’s a statement devoid of any actual purpose other than trying to make a splash.

                                            Want proof? Google “Java is dead” and then actually look at just HOW MUCH JAVA is being written every day, all the time, in enterprise environments.

                                            1. 0

                                              I don’t claim that Python is dead because I don’t like it. The opposite is true, but why put a lot of time and effort into learning a language that is degrading and where the maintainers obviously don’t care about the users. I am looking for alternatives and I think a lot of people are. That’s why I believe it’s dead. Golang looks promising.

                                              1. 3

                                                a language that is degrading

                                                Have you looked at the new Python 3 stuff? Things have been getting awesome, especially with Python 3.5 and beyond.

                                                1. 1

                                                  I guess I haven’t really %]

                                            2. 5

                                              By some measures, python is currently the third most popular language on github, behind Javascript and Java: https://octoverse.github.com/. Just saying “Python is dead” is low-effort. Please back up your extraordinary claims or expect downvotes.

                                              1. 1

                                                Fair enough.

                                              2. 1

                                                Why they have destroyed a perfectly nice language is beyond me.

                                                I disagree with this. That language was never nice. Python quiz: does a datetime test true or false?

                                                1. 1

                                                  Python quiz: does a datetime test true or false?

                                                  I’m curious where you were going with this. datetime values are True in both Python 2 and 3, as you would expect. Are you saying they shouldn’t be? Or that it’s ambiguous? Or something else?

                                                    1. 1

                                                      That issue is with regard to time not datetime (bool(datetime(2016,12,6,0,0)) is True) and that behavior both intentional and in line with what you’d generally expect: the time type’s “zero” value is falsy. I think there are many things to complain about when it comes to Python (and I like the language!) but this is not one of them.

                                                      1. 2

                                                        All of the comments on that bug report, and the fact that there is a bug report, disagree with you. I’m not sure why I’m being downvoted for “trolling.”

                                                        1. 1

                                                          in line with what you’d generally expect

                                                          It most certainly is not, which is why that bug was declared as such and fixed. Midnight is not “zero time” in any meaningful sense.

                                                          I agree that bug isn’t really serious criticism of Python; it’s a design decision that was insufficiently well-considered early on, then eventually revisited and changed. There are tons such in any language, and this one is only notable for sounding silly in summary.

                                                        2. 1

                                                          IMO some types can be coerced to a logical type legitimately and others can’t. I’d prefer that datettime.time objects raised TypeError instead.

                                                      2. 1

                                                        IMO Python is hated by so many hard core programmers because it’s not meant for you. It’s meant to be the programming language for the rest of us, and that shows in its design.

                                                        1. 1

                                                          Who do you mean by “the rest of us”? Have you ever tried to format a Unicode string in Python 3?

                                                          1. 1

                                                            u = ‘abcdé’ u ‘abcdé’

                                                            Next problem?

                                                            (Note: I realize this isn’t what you’re talking about, but - glib calls to glib :)

                                                            1. 1

                                                              I guess I was trying to make the point that Python 3 isn’t that great for noobs either - despite being used a lot as an introduction language.

                                                              1. 1

                                                                I disagree, and would argue that universities and other teaching programs all around the world use Python as their intro to programming languages for a reason.

                                                    1. 4

                                                      For python there’s NumPy:

                                                      >>> import numpy as np
                                                      
                                                      >>> a = np.array([1, 2, 3])
                                                      >>> b = np.array([4, 5, 6])
                                                      
                                                      >>> a + b
                                                      array([5, 7, 9])
                                                      
                                                      1. 2

                                                        Or plain old map:

                                                        >>> import operator
                                                        >>> map(operator.add, [1, 2, 3], [4, 5, 6])
                                                        [5, 7, 9]
                                                        
                                                      1. 2

                                                        Romania, UTC +2

                                                        1. 1

                                                          You should try Chris Done’s SHM. It’s a really nice way of manipulating Haskell structure. It’s basically like Paredit but for Haskell.

                                                          For anyone else who is getting the indentation popup, you can get rid of it by setting a local line-indent-function like so:

                                                          (add-hook 'haskell-mode-hook #'(lambda ()
                                                                                           (setq-local indent-line-function #'indent-relative)))
                                                          
                                                          1. 1

                                                            I didn’t like paredit when I was a Clojure user - too rigid. I was talking to Done last night about HIM anyway.

                                                            I’ll sharpen the saw when I’m less worn down, maybe in a couple of weeks.

                                                          1. 5

                                                            I spend most of my time in Emacs. I’ve set it up so that I can do anything development-related directly from within it - I use it to edit code, as a file manager, to manage git repos (Magit is fucking awesome) and as a terminal emulator. It’s also what I use to organize my life (Org mode + agenda mode).

                                                            I use Skype for personal IM and HipChat for work-related IM. I also use Screenhero for pair programming, Safari for web browsing, Mailplane for email and work calendar. And that’s about it.

                                                            1. 2

                                                              org-mode is seriously awesome and addictive. (I enjoy vanilla org-mode, + agenda, org-habit, org-drill, and various other circles of Org’s Inferno.) Curating my org-workflow is seriously encroaching on other aspects of my life and I love every meticulously tree structured second of it.

                                                            1. 4

                                                              I would have loved to see an example at the end where the format string comes from user input. I’ll try to find the answer myself as I’m quite baffled right now as how would Idris be able to calculate a type from a string that isn’t known at compile time…

                                                              Except for that, nice video! Thanks for creating it.

                                                              1. 2

                                                                The thing I found unsatisfactory was when there were no arguments, that was not a compile error. But extremely cool.

                                                                1. 5

                                                                  I haven’t used Idris at all so this is just an assumption on my part. But I think the reason why it didn’t error on the case where there were no arguments is because of currying. printf "%d" by itself just returns a function that takes an Int and returns a String so you can do things like:

                                                                  intPrinter :: Int -> String
                                                                  intPrinter = printf "%d"
                                                                  

                                                                  In a complete program, the compiler would complain if you tried to use the intPrinter function as a String value (thus forcing you to provide an argument). Again, this is just an assumption on my part; maybe Brian can correct me if I’m wrong.

                                                                  1. 2

                                                                    It would be an error to do the following:

                                                                    h : String
                                                                    h = printf "Hello ℅s"
                                                                    

                                                                    But the Idris REPL has no problems printing functions, unlike the Haskell REPL.

                                                                  2. 1

                                                                    I read your question initially and thought, “Clearly that’s simple to solve, you just calculate the function on the fly as you do and then pass it the relevant arguments an–” then I realized you can’t know how many arguments to take without added work. I don’t know Idris, but I suspect the problem can be written:

                                                                    main = do
                                                                      formatStr <- getStr
                                                                      printf formatStr "some" "args" "here" 1 2 3
                                                                    

                                                                    The question being, ‘What happens when the format string has some number of placeholders not equal to 6’. Certainly we could count and verify the number of needed placeholders, but I suspect this code would either compile just fine and crash on bad input, or hollar that it couldn’t figure out the type of (printf formatStr) and fail to compile (hopefully the latter).

                                                                    I suspect to get it to compile you would need to annotate the type as you do in haskell, and then you’d probably need to add a run-time check to make sure things failed nicely.

                                                                    1. 2

                                                                      Idris won’t compile the above - proving that the input string has the correct interpolation holes would be pretty tricky, especially in the way this printf is formulated.

                                                                      If you want the printf value to be determined at runtime, I’d probably just go the Haskell way but add a validation type. Not a lot of value in proving correct arguments to a value when you don’t know anything about the possible value (conversely, you gain value from having more constraints on the possible input value).

                                                                  1. 4

                                                                    With a little misdirection on his part, I wonder if this article would have ever been written. There isn’t a lot there to tie Mr. Nakamoto to the project. The money quote that he lets slip is really the anchor of newsweek’s story.

                                                                    “I am no longer involved in that and I cannot discuss it,” he says, dismissing all further queries with a swat of his left hand. “It’s been turned over to other people. They are in charge of it now. I no longer have any connection.”

                                                                    edit: redundant link

                                                                    1. 4

                                                                      That quote could easily have been the answer to a question about his government contracting work and not bitcoin.

                                                                      1. 1

                                                                        Good point.

                                                                      2. 2

                                                                        Agreed. I wonder if he’s not pulling a prank.

                                                                        Also, I wonder how that reporter got the police to go with her.

                                                                        1. 2

                                                                          She said that when she went to his house, he called the police, saying she was endangering him. I imagine his life expectancy is pretty short now; how often do you have half a billion dollars stored in a regular house in the suburbs, by somebody who has few friends and no security guards?

                                                                          1. 2

                                                                            Oh, got it.

                                                                            This guys needs to cash out some BC and move his family to the Grand Cayman

                                                                            1. 1

                                                                              I’ve seen this idea elsewhere too, and it’s sometimes given as meaning that Satoshi can just protect himself if he wants to. But there are several things wrong with such an argument:

                                                                              (1) He might not still have access to the BTC. Even if he does, Newsweek don’t know that

                                                                              (2) He might not even be the BTC Satoshi; it’s not established beyond all doubt

                                                                              (3) Moving house, changing your life, is hard at the best of times, and Satoshi is ill—a fact known to Newsweek before publication

                                                                              (4) Satoshi appears to have made the commitment not to spend his BTC. Why force him to spend his own money on security that he ought not to have needed?

                                                                              (5) As you point out yourself, it’s not only Satoshi’s security at stake here. Does anyone think that the whole Satoshi family should have to change their lives over this?

                                                                              1. 1

                                                                                Not to mention that a life in the Grand Caymans doesn’t appeal to everyone. Maybe he’s happy with his life there (or at least he was)?

                                                                                1. 1

                                                                                  I didn’t mean to imply that Newsweek’s behavior is somehow defensible. Or that it will be easy for this person to run away to saftety.

                                                                                  This Satoshi and his family are basically in for deep upheaval. It’s sortof like a popstar becoming super famous, but not quite rich yet. Dealing with it will be extremely difficult for him and his family (hopefully he does have access to the Bitcoins).

                                                                                  Does anyone think that the whole Satoshi family should have to change their lives over this?

                                                                                  Rich people do exist in this country (even richer than this guy). And, in general, I don’t think that their families have to live in fear all the time, or live in something like “witness protection”. The police crack down real hard on kidnapping and other types of extortion. Otherwise, everyone would be a criminal.

                                                                                  1. 1

                                                                                    When I was in Sligo, Ireland back in 2000, I met this guy on the dole who invited me over to a house party. We were walking around the neighborhood and he pointed out the house of the local millionaire. It was basically the same design as his house, and it was simply on the corner at the end of the same block.

                                                                        1. 1

                                                                          This always bothered me about haskell. Why is it it so hard to read a line from a file? I understand that because of jumping through all of these hula hoops we end up in situations where the compiler can suddenly magically do very cool things, but it feels like so much effort. I’m sure it’s effortless after you’ve internalized the state monad and the io monad and the writer monad and the reader monad, but until then, it still feels like a pain.

                                                                          1. 7

                                                                            This has nothing to do with reading a file. There is nothing about the IO monad in the article.

                                                                            Reading a file is not hard:

                                                                            main = do
                                                                              content <- readFile "/tmp/test.txt"
                                                                              let firstLine = head (lines content)
                                                                              putStrLn firstLine
                                                                            

                                                                            Done.

                                                                            Where was the effort?

                                                                            1. 5

                                                                              Since my above Haskell is actually async, let’s talk about real effort:

                                                                              var fs = require('fs');
                                                                              
                                                                              fs.readFile('/tmp/test.txt', 'utf8', function(err, content) {
                                                                                  var firstLine;
                                                                              
                                                                                  if(err)
                                                                                      return console.error('Error loading file');
                                                                              
                                                                                  firstLine = content.split(/\n/)[0];
                                                                                  console.log(firstLine);
                                                                              });
                                                                              
                                                                              1. 3

                                                                                This also seems like a pain in the ass, I would not like to do IO things in this language either.

                                                                              2. 1

                                                                                If I didn’t want to putStrLen at the end, but instead wanted to do something else, would it be so easy? I might be misunderstanding, but my impression is that putStrLn eases my way here because putStrLen wraps firstLine in the IO monad for me.

                                                                                1. 2

                                                                                  I think you might be misunderstanding. The putStrLn function takes a String and returns an IO action. What other than printing to the screen would you like to do? The last line could be anything!

                                                                                  main = do
                                                                                    content <- readFile "/tmp/test.txt"
                                                                                    let firstLine = head (lines content)
                                                                                    name <- getName
                                                                                    printMessage name firstLine
                                                                                  
                                                                                  getName = do
                                                                                    putStr "What is your name? "
                                                                                    readLn
                                                                                  
                                                                                  printMessage name line =
                                                                                    putStrLn ("You are " ++ name ++ " and the first line is " ++ line)
                                                                                  
                                                                                  1. 1

                                                                                    I must be misunderstanding the type constraints. My impression is that I need the do loop to end with an IO. What if I wanted to parse the text file and return the sum of a bunch of numbers in the text file? If I could figure out how to wrap the number in an IO monad again, that would make it typecheck.

                                                                                    I could just be badly misunderstanding do loops.

                                                                                    1. 1

                                                                                      You’re after the return function. Will take any value and wrap it in IO.

                                                                                      1. 2

                                                                                        Gotcha. I guess I just had too much trouble with the haskell docs. My impression was that I could find everything I needed to know about IO here, but clearly I should have used my google-fu and ended up here. Thanks for the clarification!

                                                                                        That definitely makes the IO monad easier to work with, although it still seems onerous to me. I think I’ll have to force myself to do more with it in order to internalize how it works before it seems simple.

                                                                              3. 4

                                                                                It’s not hard to read lines from a file as Brian showed. Have you used Haskell before or do you just think that it’s hard to do trivial tasks like that?

                                                                                1. 1

                                                                                  My main experience with the IO monad was trying to parse commandline options, which I found deeply frustrating. Definitely part of the problem was my inexperience with haskell, since before this I had only done trivial work in it before. However, it seems unreasonable to me that it should be so complicated.

                                                                                  More precisely, I like that in Scala I can just look up io.Source.fromFile(“filename”).getLines (or something similar) and it hands me back an iterator, instead of an IO iterator which I have to be super careful with, especially since it isn’t clear to me how to unwrap an IO a la

                                                                                  val x = unwrap IO(lines)

                                                                                  edit: I am aware that my example of having a hard time with the IO monad is godawful haskell.

                                                                                  1. 2

                                                                                    You can’t completely unwrap IO, that’s the point. You always want to wrap it back up with map or flatMap/bind/chain. That’s exactly what do-notation does:

                                                                                     main = do
                                                                                       content <- readFile "/tmp/test.txt"
                                                                                       -- "content" as being unwrapped
                                                                                       let x = lines content
                                                                                       -- wrap it back up with "print"
                                                                                       print x
                                                                                    
                                                                                    1. 1

                                                                                      I am having trouble understanding how I do this:

                                                                                      read data from a file
                                                                                      get out of io land with that data
                                                                                      manipulate that data
                                                                                      later, output the result of manipulating the data.

                                                                                      The impression I have now is that it is illegal to leave IO land with that data, unless it is safely wrapped in an IO. If I wanted to do something like this, instead the right way would be to say something along the lines of

                                                                                      let x = read data  
                                                                                      let y = map x manipulate  
                                                                                      -- I don't know map syntax in haskell  
                                                                                      do  
                                                                                        manipulated <- y  
                                                                                        putStrLn manipulated  
                                                                                      

                                                                                      it seems like a pain to have to add an extra level of mapping every time I want to manipulate something that came from outside. I have heard that this is to protect us from the outside world since it is impure, but I suppose I just haven’t internalized it.

                                                                                      1. 1

                                                                                        The above example does almost the 4 things you listed. Let’s go line by line:

                                                                                        main = do
                                                                                        

                                                                                        read data from a file

                                                                                        content <- readFile "/tmp/test.txt"
                                                                                        

                                                                                        Now here’s the thing – content is not an IO value but it’s also not “out of IO land” because it can’t escape (the whole do expression must return an IO).

                                                                                        manipulate that data

                                                                                        let x = lines content
                                                                                        

                                                                                        later, output the result of manipulating the data.

                                                                                        print x
                                                                                        
                                                                                        1. 1

                                                                                          I understood that part. What I didn’t understand how to do was how to carry around the IO in a subsequent expression.

                                                                                          1. 3

                                                                                            Maybe this will help clarify things:

                                                                                            import Data.Char
                                                                                            
                                                                                            shout :: String -> String
                                                                                            shout line = map toUpper line
                                                                                            
                                                                                            getLines :: String -> IO [String]
                                                                                            getLines filename =
                                                                                              do content <- readFile filename
                                                                                                 let ls = lines content
                                                                                                 let shoutyLines = map shout ls
                                                                                                 return shoutyLines
                                                                                            
                                                                                            main :: IO ()
                                                                                            main =
                                                                                              do shoutyLines <- getLines "example.in"
                                                                                                 let joinedLines = unlines shoutyLines
                                                                                                 putStrLn joinedLines
                                                                                            

                                                                                            getLines :: String -> IO [String]

                                                                                            getLines takes in a String and, if it is ever run, does some IO and returns a list of Strings. In this case, if it is run, it reads all the lines from a file into a list of Strings, converts each of those strings to uppercase and returns (lifts it into a Monad context) the new list.

                                                                                            By the way, the above code could also be written as (and it actually gets expanded into something very similar since do notation is just syntactic sugar):

                                                                                            import Data.Char
                                                                                            
                                                                                            shout :: String -> String
                                                                                            shout line = map toUpper line
                                                                                            
                                                                                            getLines :: String -> IO [String]
                                                                                            getLines filename =
                                                                                              readFile filename >>= \content ->
                                                                                                return $ map shout $ lines content
                                                                                            
                                                                                            main :: IO ()
                                                                                            main =
                                                                                              getLines "example.in" >>= \shoutyLines ->
                                                                                                putStrLn $ unlines shoutyLines