6 ms·
Essentially, you're saying that the point is not to use DSL's. The benefits of DSL's are explained everywhere, so I don't think I need to repeat them here...
by Peaker 13y ago
Essentially, you're saying that the point is not to use DSL's. The benefits of DSL's are explained everywhere, so I don't think I need to repeat them here...
Parser combinators are not what you said they are -- and you won't be able to write the kinds of things you can in Haskell. e.g, to parse 10 comma-followed-by-int:
replicateM 10 (parseComma >> parseInt)
How would you implement that in Go? Even if you violate DRY and re-implement replicateM in every monadic context I don't think Go will be able to encode anything similar in power to parser combinators.
> But I have a nagging feeling that adding logging or exceptions in this context is quite a bit more convoluted
To add exceptions, I use an EitherT transformation on my Transaction monad.
To do a debug log, you just insert an impure debug log as you would in a non-pure language.
To insert an actual production log you would use a Writer that accumulates the logs in order to eventually write them outside the transaction context. Otherwise, aborted transactions will also have the production logs. If you want these semantics, add logging capabilities to the transaction monad. Otherwise, you get nice guarantees about what can't happen.
- jbooth 13y agoBut why the hell get into all that? We've got a job to do. Your comment here is a great example of why people don't bother giving Haskell the time of day. I've already got business problems and performance problems, why give myself type system problems too? You're talking about adding on all these layers of complexity and abstraction, and the benefit is more "pureness". What do I care about pureness? I'm writing business code, or unix code, it's not going to be pure either way. You'll claim that the type system makes all of the business problems just go away magically because your type system has reached a skynet level of self-awareness, but we both know you're gonna be debugging the same crap at the end of the day, except now you have 12 different monads, type constraints and a homegrown DSL in between you and the problem. I'd prefer to work with a simpler environment, and it doesn't make me "too dumb to understand haskell". It just makes me "more productive than if I were working in haskell".
- Peaker 13y ago> why give myself type system problems too? Exactly! Why use Go and have type system problems? Chase nil bugs in the middle of the night, when my type system could have caught them all for virtually no cost at all? Why use Go and have type system problems like lack of sum types and pattern matching, having to waste my time emulating them with enumerated tags or clunky type switches? What you're calling "layers of complexity and abstraction" are just "layers of abstraction" -- Haskell code to solve a problem tends to be simpler than Go code to solve the same problem. By simplicity, I'm talking about mathematical simplicity here. Not ease of learning. Simplicity is hard. But it pays. I don't claim that the type system makes all problems go away magically, but it helps catch a far greater chunk of the errors. > we both know you're gonna be debugging the same crap at the end of the day Actually, no. If you've actually used Haskell, you'd know that debugging runtime problems is a much much more rare phenomenon. It happens, but it's pretty rare. I don't ever debug null problems. I almost never have to debug any crashes whatsoever. I don't debug aliasing bugs. The vast majority of the sources of bugs in other languages do go away. > I'd prefer to work with a simpler environment, and it doesn't make me "too dumb to understand haskell" Who claimed you're "too dumb to understand Haskell"? If you're smart enough to write working Go programs, you're most likely smart enough to learn Haskell. But learning Haskell means learning a whole bunch of new useful techniques for reliable programming, and that isn't easy. People who come to learn Haskell and expect it to be a new front on the same concepts they already know (e.g: like Go is) are surprised by how difficult it is -- because it isn't just a new front. There are a whole set of new concepts to learn. This set isn't really larger than the set of concepts you already know from imperative programming, but the overlap is small, and you forget just how involved what you already know is.
- jbooth 13y agoHey, this is a pretty late response but regarding: "I don't ever debug null problems. I almost never have to debug any crashes whatsoever. I don't debug aliasing bugs. The vast majority of the sources of bugs in other languages do go away." I think this is the red herring at the heart of the problem. Those bugs really aren't a big deal, they happen rarely once you're proficient and they are quickly solved on the rare occasion when they do happen. I'm talking about logic bugs, the kind that your compiler isn't going to find, or even that a "sufficiently smart compiler" couldn't find because it's a misunderstanding in the specification that you have to bring back to the product owner for clarification. Or bugs that occur when 2 different services on different machines are treating each other's invariants poorly. Those are the bugs I spend time on. I haven't spent any time at all with Haskell, really, but it seems like a poor trade off to have to learn a bunch and engineer things in a way that's more difficult in order to prevent the easiest bugs.
- jejones3141 13y agoHmmm... not quite a flashback. If it were a flashback, you would have written "C" instead of "Haskell", and would have been thinking of assembly language rather than C or C++. You have the same problems no matter what; the question is whether you want a tool that helps with them, or one that doesn't.
- pacala 13y agoThere is one construct for repetition in Go. It's called "for". parser = ParserNil; for i := 0; i < 10; i++ { parser = ParserSeq(parser, ParserSeq(parseComma, parseInt)) } PS. I'm a complete Go newbie. Don't take this code as the "one true Go way".
- Peaker 13y agoSo you're forced to duplicate the monadic combinators (e.g: inlined replicateM here). Now let's consider: myParser = do logDate <- date char ':' logTime <- time let fullTime = fromDateTime logDate logTime msg <- if fullTime < newLogFmtEpoch then do str <- parseString return (toLogMsg str) else do idx <- parseLogIndex getLogParser idx Translating this to bare-bones Go would require using explicit continuations everywhere. Any combinator you use from Control.Monad is going to be duplicated/inlined in your Go code, violating DRY repeatedly.
- pacala 13y ago> forced to duplicate the monadic combinators (e.g: inlined replicateM here). ??? This is a bare bones for loop. What exactly do you consider "duplication" ??? Regrettably, I can't accept the challenge because I have no idea what the code you pasted is doing and why.
- Peaker 13y agoA bare bones for loop is the implementation of replicateM. There are more complex combinators than replicateM, e.g: filterM, zipWithM, and more... Which you would need more than duplicating a bare bones loop at every occurence. The code I pasted above uses "do" notation to write a monadic parser. The parser parses the format started by a <date>:<time> and if those set a date larger than some point in the past, it parses the continuation of the text differently. Which part of the code are you having difficulty understanding?
- redbad 13y ago> Essentially, you're saying that the point is not to use > DSL's. The benefits of DSL's are explained everywhere, > so I don't think I need to repeat them here... DSLs aren't a gimme. In production code, produced and maintained by a team, and iterated through a lifetime of morphing business requirements, DSLs are almost always more of a hindrance than a help, because they impose an additional cognitive burden on their manipulators. They're lovely to write, and elegant in a closed system, but in The Real World(tm) where we all live and work, we don't generally have the luxury of writing software to solve those classes of problems.
- Peaker 13y agoThis is called the "Real World" fallacy. Your techniques are unfamiliar to me, and I am in the "Real World", therefore you are an academic who doesn't actually solve real world problems. DSLs are used in production code, and solve real world problems better than ad-hoc repetative code does.
- redbad 13y ago> DSLs are used in production code, and solve real world > problems better than ad-hoc repetative code does. And this is called the "argument by assertion" fallacy. I'm sure there are a lot of problems where DSLs make sense to use. They're simply not _most_ problems.
- bad_user 13y agoWhenever you're creating a function, you're defining a verb in your domain-specific implementation. Whenever you're creating a class / interface / prototype, you're defining a noun in your domain-specific implementation. To me, the usage of the term itself ("DSL") does not make much sense. Using a combination of public or custom libraries and APIs is in itself a DSL and the combination makes it unique, per application. And when programming, you're extending that language all the time. That's what you're doing with every function, class or interface you add. That's what programming is - specifying to the computer how to do computations by building a language made of nouns and verbs that it can understand and then forming sentences out of those nouns and verbs. And these definitions transcend the actual lines of code, as when you're communicating to your colleagues, in speech or in writing, like in emails or specs, we do need precisely defined words to refer to concepts within your app. The term DSL in the context of software-development is basically a pleonasm. And discussions on DSLs are actually stupid, as people argue about a non-issue. The real discussion should be - in what contexts do you really need re-usability and/or composability and/or succinctness? Not always, I'll grant you that. And here, I think we can learn from mathematics or physics, spanning domains so complex as to be intractable without defining mini-languages to express things efficiently. Speaking of Monads, many people described them in terms of mathematics, like with the infamous "a monad is a monoid in the category of endofunctors". You could say monads are just a design pattern, with some simple properties to grasp and some examples and normal people wouldn't need more to understand their usage, however understanding their mathematical underpinnings, that use big and unfamiliar words that scare us, allows one to grok the notion and build on top of it bigger and better abstractions. And abstractions help us to tackle even more complex problems. Yes, even in the real world.