So did he ever figure out the original question about why most languages don’t have support for rudimentary sums in the same way there are for tuples? i.e., types such as Int + Int
, Int + String
, etc. Maybe once you’ve generalized to variants there’s not much reason to also support sums.
Yes, they found that even “anonymous” product types are actually built from a constructor like Haskell’s (,)
. That is, your Int + Int
type could be viewed as sugar for Haskell’s Either Int Int
, and similar sugar could be used in pattern-matching.
They don’t spell it out, but memory layouts for languages like Haskell and OCaml would use the same technique for these “anonymous” sugared sum types as for any other user-defined sum type, with tagged unions.
personally, I assume the reason there’s no ad-hoc sum-type similar to tuples is syntax. Without labels there’s no obvious way to destructure a value; say x
has type Int + String
, you could do something like
case x of
Int i -> foo
String s -> bar
i.e. using the types as labels. However, this wouldn’t work for a type like Int + Int
, where there are duplicate types. Now, since types are nominal, so String + Int
would be different from Int + String
, one could just drop the labels and match based on ordering. However, I think most people would agree that that would be quite detrimental to code readability, and that there’d be little added value compared to using ADTs.
It’s worth noting that TypeScript, which uses structural typing, does have ad-hoc sum types: you can do const x: string | number = "123"
. You can even make a tagged union using something like
type MyEnum = { kind: "foo"; value: string} | { kind: "bar"; value: number }
function f(x: MyEnum) {
if (x.kind == "foo") {
// typechecker knows x.value is a string
}
}
because the compiler has really good control flow analysis.
I’ve picked up reading Types and Programming Languages after having read the Structure and Interpretation of Computer Programs in college followed by the Essential of Programming Languages. Having spent nearly 9 years as a software developer, I can’t say any of it has been useful. I’d have probably done better to interest myself more in the business problems at hand than to expend energy reading through these books that are too academic for most programmers. What’s more, I’m pretty certain that I’ve met other programmers who are blissfully unaware of recursive data types, regular expressions, or anything else that programmers could pride themselves on knowing but they’re better than me at getting code working, simply and without fuss. Maybe because they’re better problem solvers or maybe because their view of programming hasn’t been crowded out with theory.
But what keeps me going, or what I tell myself, is that the occasional opportunity does come up in which this exposure to theory results in some direct or amusing application. It does happen and then I feel that my time hasn’t been wasted. Could also just be intellectual wankery on my part though.
Well, I don’t think it’s wasted if your aim is to learn to build languages – as Steve Yegge says, everyone should learn to build a compiler. However for people who want to jump into it I would recommend the OWASP cheatsheets – which are far more relevant to programming practices than most of this fluff.
What kind of applications do you develop? I’ve certainly used a little bit of theory here and there, and where I did have to use it I probably wouldn’t have been able to get the job done at all (or in any reasonable amount of time or with reasonable results). But not even needing regular expressions, that sounds to me like a very strange situation.
Maybe you’re simply doing work for which you’re overqualified?
Let me save you five minutes of reading a buzzfeed list in the form of an ACM queue article:
- Set your alarm and wake up at the same time each day.
- Shower and dress as if you’re going to the office as you normally would.
- Set a finishing time for each day and stick to it.
- Take frequent breaks of at least 15 minutes per two-hour block throughout the day.
- Silence all your messaging apps.
- Do not use social media during your breaks.
- Arrange social time with actual humans outside of work.
- Exercise.
- Use your old commute time to learn something new.
- If you schedule meetings, make them count.
I found the article fun enough to read. But this this was the first thing I’ve ever read by Kode Vicious.
I did none of these things structurally while working remotely for over 5 years and it was fine. Do not follow authority blindly. Figure out what works best for you.
It might be just me, but it seems like Rails Engines is missing in the discussion. How does Packwerk compare to that approach?
I think the core issue they are addressing is not whether components of an application with separate concerns be isolated for configuration, convenience, development or testing. That can certainly be done with Engines, or even with a small team and a strong commitment to internal organization/conventions.
This seems to be “solving” something that lies at a much deeper level, and which those two approaches would leave unsolved (or tool-assisted variations thereof, as discussed in the post). Namely, that Ruby constant resolution allows for you to reference any constant, anywhere, for any purpose, after it is loaded (from anywhere else even if you didn’t know it!). It is the difference between what happens (in all every other module/file/context) after a call to Ruby’s “require” versus after a call to Python’s “import”.
One could take a self-contained grouping of four models, two controllers, and a few interactors, make them into an Engine, and document that “This mini-domain should be treated as an external API, and only accessed as such!” However, there is nothing stopping someone in the primary application from just referencing one of those models by name deep in some other application code, completely invalidating that attempt at isolation.
This kind of problem can be enforced with some discipline (docs, code review, or otherwise), but the likelihood that this gets violated is one that scales with team size, codebase size, and rate of change within a Ruby project. Just as noteworthy: these are all efforts that would not be required in other ecosystems, where there is a more restrictive answer to the question “Which constants are available in my current execution context?”
I have thought about this often as one of the biggest challenges in working with Ruby projects “at scale”, and for my particular areas of interest, this is more of a factor weighing against Ruby than the oft-discussed topic of performance.
To add to my initial comment, I’ve seen posts from other companies that describe how they’ve managed to use engines to encapsulate and enforce interfaces between parts of their application. Flexport and Root Insurance come to mind.
Hi! This is a great topic that I should have included in the blog post. Rails Engines is definitely one of the mechanisms you can use to modularize a Rails application. As @swifthand mentioned below, Packwerk is a tool to enforce boundaries. While Rails Engines comes with other functionalities, it can only be used to establish boundaries. The modularity isn’t enforced because constants will still be globally accessible.
You can try using both Packwerk and Rails Engine in a Rails app though. What do you think?
I also highly recommend checking out the Rails engine section in my colleague’s blog post - https://engineering.shopify.com/blogs/engineering/shopify-monolith
AWK can be good for prototyping an idea, but you (very) quickly run into its limitations. No typing, no array literal, functions arent first class citizens, cant pass arrays by value, no imports. Its even missing basic functions like array length.
But biggest negative is the myriad implementations: NAWK, MAWK (2 versions), GAWK. Makes it very difficult to write portable code.
AWK can be good for prototyping an idea, but you (very) quickly run into its limitations.
If I consider when AWK was created (1977), I must say, that it is incredibly well designed and successful piece of software. Yes, it is sometimes ugly, sometimes limited …but it is still in use after 44 years! God bless Alfred, Peter and Brian.
(regardless we usually use the GNU implementation, it is still based on the original idea and language)
AWK and classic unix approach is quite limited when it comes to structured data. But we can push it bit further and improve by borrowing ideas from relational data model – and still use classic tools like AWK.
funny enough, gawk’s –lint option will let you know what constructs are gawk (not posix awk) specific which helps with your biggest negative case. if you use vim, ALE for (g)awk scripts will highlight them inline.
AWK can be good for prototyping an idea, but you (very) quickly run into its limitations.
A good programmer can work around these limitations. Just look at dwatch(8) on FreeBSD. Heavy use of awk.
https://svnweb.freebsd.org/base/head/cddl/usr.sbin/dwatch/
Or how about an HTTP caching proxy in gawk?
A good programmer can work around these limitations.
“Should they?” is a better question. They’re better off using a powerful tool that doesn’t limit them. Then, limit their use of it to what they need for maintainability. Subsets, DSL’s, and so on.
Shell script with embedded awk in functions paired with fd redirection AND eval’ed sudo. That looks like a maintenance nightmare for anyone who’s not the original author.
It was reviewed and signed off by three other core developers, so I don’t think that’s going to be a problem.
I don’t write awk for work, more so for pleasure, and its limitations can make it fun to use. It clearly was influential on the languages we use today and it would be interesting to see a programming historian trace that lineage.
related: https://gerda.tech/
unrelated: https://twitter.com/pi0cket
I own a Nokia 8810, and I can’t really recommend it from a hardware perspective. My fingers aren’t very big, but I still have trouble hitting the keys accurately. Not to mention that KaiOS is really slow (at least on this particular Nokia), and has issues with doubling keypresses when writing.
I haven’t tried Gerda myself, but considering the amount of pre-installed applications and games that cannot be uninstalled, I’d likely recommend it.
Nokia has some newer KaiOS phones available that seem much better from a hardware usability perspective, like this flip phone and a “tough” phone.
a “tough” phone.
…
Pre-loaded Facebook and WhatsApp
ffs. pretty sure anyone who cares about privacy won’t want to be in the same room as that.
anyone who cares about privacy won’t want to be in the same room as that.
True, but it’s also true that you’re not forced to configure run those applications or even run them.
I like the idea of having tethering available: it is definitely useful to be able to connect with a device you trust more (e.g. linux laptop) for anything more complex than sending an sms.
I got try out the new Nokia 8110 recently, and obviously it’s just as cool (from a purely superficial perspective) as ever. Like craftyguy mentioned, it’s loaded with Facebook and Whatsapp. While I don’t care for Facebook, I put up with Whatsapp because of its ubiquity (I’d rather not, but there we go).
It’s not perfect, but at the moment I’d say KaiOS is as close as you’re going to get to a simple phone with “necessities”.
People should be able to choose to use services like those with minimally invasive clients.
After all, privacy (and security) means retaining control on what personal data we give and to whom.
This is even better in F# (compiled to ECMAScript with Fable). I highly recommend evaluating Fable for your ES needs if you are currently using TypeScript but like this sort of thing.
type MyAdt =
| Option1 of fooField : string
| Option2 of barField : float * bazField : string
| Option3 of fooField : float * bazField : string
let doSomething (value : MyAdt) =
match value with
| Option1 (fooField) ->
printfn "Option 1 fields: %s" fooField
| Option2 (barField, bazField) ->
printfn "Option 2 fields: %f, %s" barField bazField
| Option3 (fooField, bazField) ->
printfn "Option 3 fields: %f, %s" fooField bazField
or, perhaps slightly less conveniently, with records:
type MyAdt =
| Option1 of
{|
fooField : string
|}
| Option2 of
{|
barField : float
bazField : string
|}
| Option3 of
{|
fooField : float
bazField : string
|}
let doSomething (value : MyAdt) =
match value with
| Option1 fields ->
printfn "Option 1 fields: %s" fields.fooField
| Option2 fields ->
printfn "Option 2 fields: %f, %s" fields.barField fields.bazField
| Option3 fields ->
printfn "Option 3 fields: %f, %s" fields.fooField fields.bazField
We don’t need the “impossible” case because the compiler (naturally) checks pattern matches for exhaustion.
I hope they add something like pattern matching to make checking through the cases of the ADT a bit less cumbersome. Given how much ceremony it takes to define and use an ADT, I sometimes wonder if I’m even doing the right thing.
The switch statement is kind of a weak pattern matching. It doesn’t allow you to match on the fields or extract field values easily. I still consider it a good tool for your toolbox, specially if you do Redux. In Redux, you have ADTs, either explicitly declared, or implicitly declared. I prefer explicit in terms of types, so at least the typechecker can spot some of my mistakes.
For technical stuff, I’ve had good luck starting with a book or paper and following its references and so on. Other times I’ll read things just because a knowledgeable person recommends it. Having found some great books that have been overlooked due to age or whatever, I don’t place too much weight on popularity or Amazon reviews.
Guess I could never aspire to model myself on him… It would be leap too far for my abilities, and a leap too far from my day job too.
Definitely, I feel the same way if we’re talking about achievements. In my mind he brings together a surprising mix of things: technical ability, humility, dedication and a surprising amount of self-deprecating humor.
“‘If you think you’re a really good programmer… read (Knuth’s) Art of Computer Programming… You should definitely send me a résumé if you can read the whole thing,’ read a quote from Bill Gates on the cover of the third edition of the first volume.”
LPEG, the Lua pattern-matching tool based on PEGs, translates patterns into programs that are interpreted by a parsing machine. Here’s the paper where they go into details about it (section 4): http://www.inf.puc-rio.br/~roberto/docs/peg.pdf
I use LPeg a lot. One example, I created an LPeg expression that generates another LPeg expression to parse dates based upon the format string used by strfime()
. (Code).
The analysis would be more interesting if the algorithms described were designed with the same use case in mind.
This part was never hard to get for me: I unterstand how monads are defined, how they are used and that they let me write a pretty sequences of Maybe
(or in this case attempt
) operations.
But what I don’t get is 1. what all the singular monads practically have in common, in the case of Haskell for example: IO, State, Maybe, etc., 2. why monads are necessary (if they even are) for pure functional programming languages. 3. In what sense is this related to Monads from philosophy (simplest substance without parts)
I haven’t yet ever had the experience of seeing a problem and saying “Why, I could express this as a Monad”. And if peoole will go on writing Monad articles, which I am sure they will do, I would very much appreciate it if someone could touch on these issues.
Apparently monads were considered a kind of breakthrough for dealing with some ugly things (IO, exceptions, etc) in a purely functional programming language. I would highly recommend this paper for understanding why they were introduced into Haskell https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/mark.pdf I found the historical perspective especially interesting.
- what all the singular monads practically have in common, in the case of Haskell for example: IO, State, Maybe, etc.
They have a common signature that follows the same laws. This means you can write code that works generically for any monad, and a lot of standard helper functions are already written for you, e.g. traverse
or cataM
. For a concrete example, in a previous codebase I had a bunch of per-client reports that had different requirements - one needed to accumulate some additional statistics over the report, and another needed a callback to their web API. So I wrote the general report logic generically in terms of any monad, and then for the client that needed the extra statistics I used Writer
and for the client that needed the callbacks I used Future
so that I could use a nonblocking HTTP library.
- why monads are necessary (if they even are) for pure functional programming languages.
They’re never absolutely necessary, but it turns out that a lot of the time where you’d be tempted to use impure code to solve a problem, a monad lets you use the same code style but have your functions remain pure. E.g. rather than a global variable you can use Reader
or State
. Rather than a global logging facility you can use Writer
. Rather than exceptions you can use Either
. Rather than a thread-local transaction handle you can use a transaction monad. etc.
- In what sense is this related to Monads from philosophy (simplest substance without parts)
Not at all, it’s an accident of terminology.
I haven’t yet ever had the experience of seeing a problem and saying “Why, I could express this as a Monad”.
Once you’re used to them you start seeing them everywhere.
They have a common signature that follows the same laws.
Ostensibly they follow the same laws. But sometimes people let them break the laws at the edge cases. For example, State is not a monad.
State is not a monad.
State is a monad under the usual definition of equivalence used when reasoning about Haskell (in which bottom is considered equivalent to everything). Under a more natural definition of equivalence, seq
is not a function and State is still a monad (defined only on the legitimate/categorical fragment of Haskell). To pretend that the weirdnesses of seq
and lazy evaluation have anything to do with State specifically is grossly misleading.
Nice. This paper by Philip Wadler does something similar by introducing monads through examples of their applications: http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf A bit handwavy at times, but useful because it touches on the monad laws and there’s an extended example with parsers.
I’ve seen a similar thing with tests written in rspec where lets are exclusively used in place of local variables. Any thoughts on how to strike the balance there?
YES! This advice completely applies to let
variables in RSpec tests too. A let
is a memoized method definition in my book. I am happy to recommend using more local variables over let
variables in tests.
Is there an OpenBSD port for Oz anywhere? I can attempt to compile from source, but I’m hoping there’s something easier.
Not that I’m aware of. Hopefully it’s buildable. The main issue is the code base is older C++ so some warnings/errors in current compilers need to be disabled. It’s also 32-bit only. Mozart 2 is 64 bit but lacks the constraints and distribution libraries.
I tried OCaml for a bit but the weirdness got to me after awhile. There was a ton of magic around project setup and compilation that I didn’t understand and couldn’t find properly explained, and the fact there is more than one “standard” library bugged the heck out of me. I’m hoping that once the Linux story solidifies a bit more around .NET I’ll be able to reasonably give F# a shot.
I’ve been using F# on Linux for a few years now using Mono. It’s a bit more manual than .NET Core, but it’s stable.
If you’re interested in trying again, I created a build system (yes, yet another one) specifically designed for getting going fast in most cases. I have a blog post here:
http://blog.appliedcompscilab.com/2016-Q4/index.html
Short version: all you need is a pds.conf
which is in TOML so fairly straight forward, a specific directory structure (src/<project>
) and GNU Make. Then you run pds && make -f pds.mk
and you’re done. Supports tests as well as debug builds.
I’m not sure it is worth pushing yet another build system that seemingly nobody uses (at least I haven’t yet run across a package which uses it) when jbuilder seems to be gaining so much momentum in the OCaml world lately.
Maybe, but pds is pretty easy to port away from for most builds and it’s so trivial to get started and much less confusing than jbuilder’s config, IMO. My personal view is that jbuilder is a mistake but I’ll wait to switch over to it once it’s gained enough momentum. At that point, I can just switch pds over to producing jbuilder configs instead. But I’m a symptom of the problem rather than the solution unfortunately. I also use @c-cube’s containers, so yet another stdlib replacement/extension :)
My personal view is that jbuilder is a mistake
Could you elaborate on why? IMO jbuilder is not perfect either but if we get a modern, documented build system which is hopefully easy to setup, it would be a massive win over all the other solutions we currently use.
I agree, the different choices in tooling is sort of disorienting and it can lead to analysis-paralysis. For a toy compiler project I started working on, I tried to find the most basic tooling that would work: whatever ocaml compiler came with my distro, ocamlbuild, make, and then eventually, extlib, ocpindent, and then after some more time, opam, ocamlfind, utop. It may make sense to use the tooling outlined in this article if future maintainability is a big concern, but to get started and to learn ocaml, I don’t find it necessary (and definitely not appealing). Having done this, I don’t pine so much for standardization (;
It bothers me because it makes the language more difficult to learn. It also wasn’t always clear to me that an alternative was in use because, IIRC, they’re not (always) clearly namespaced. I have run into this in Haskell as well, FWIW.
Typically it’s visible when you use an alternative stdlib because you start your files with open Batteries
or open Core
or open Containers
. I agree it’s annoying that the stdlib is not richer, and it’s a bit slow to accept contributions, but in a way the existence of alternative stdlibs/extensions shows how easy it is to roll your own :-)
Haskell, C, and D come to mind. You could also argue that Python has multiple standard libraries because it has different implementations that effectively can’t use some aspects of the normal stdlib (PyPy). Then there’s Java: SE, EE, and ME are the same language with different sets of functionality in the standard libraries.
Out of curiosity, have you tried OP’s project setup?
Also, there is only one OCaml standard library–the one that comes bundled with OCaml. The other ‘standard libraries’, Batteries Jane Street’s Core, are optional add-ons made for specific purposes.
I haven’t tried OP’s setup, but honestly it seems even worse than what I had. I pretty much followed this: https://ocaml.org/learn/tutorials/get_up_and_running.html. I ended up using Oasis, which was just awful, every time I added a file or dependency I had to fiddle with the config until everything would build again, but at least there wasn’t an entirely separate language.
From OP:
(jbuild_version 1)
(executable
((name main) ; The name of your entry file, minus the .ml
(public_name OcamlTestProj) ; Whatever you like, as far as I can tell
(libraries (lib)))) ; Express a dependency on the "lib" module
Note the comment, “as far as I can tell”. To me, that’s a terrible sign. A person who has gone to a reasonable amount of effort to explain how to set up a project can’t even figure out the tooling completely.
Jbuilder is quite nicely documented (see http://jbuilder.readthedocs.io/en/latest/). The public_name defines the name of the produced executable in the install context. It does not take much effort to read it from there
Of course you still have to find out that Jbuilder exists, which the official site doesn’t seem to mention… I am lazy, I don’t like choices, I just want one, blessed tool that works more or less out-of-the-box if you follow a set of relatively simple rules (I’m even OK with wrapping the tool in a simple, handwritten Makefile, which is what I do in Go). I’m not arrogant enough to think that the way I prefer is the “right” way, in fact in some cases it would be dead wrong (like for extremely complex, multi-language software projects), but that explains why I dropped OCaml for hobby stuff.
OK, but your criticism is that you have to find out that JBuilder exists, commenting on a post that tells you about JBuilder.
To be fair, jbuilder is very young (not even 1.0 yet actually) but it might become the “standard” build tool the OCaml community has been waiting for for years (decades?). Then clearly there will be more doc and pointers towards it.
Well obviously I know about it now, but it still isn’t terribly discoverable for someone new to the language. My actual point, and I probably didn’t make this as clear as I should have, sorry, is that in my experience OCaml isn’t very friendly to beginners, in part because its tooling story is kind of weak and fragmented.
Yeah. This is true. Especially on Windows. People are working on it but it’s slow and it’s taking time to consolidate all the disparate efforts. I myself am not getting terribly excited about OCaml native but funnily enough I am about BuckleScript (OCaml->JS compiler) because of its easy setup (npm i -g bs-platform
) and incredible interop story.
Others are getting equally into ReasonML ( https://reasonml.github.io/ )because it’s coming from a single source (Facebook) is starting to build a compelling tooling/documentation story.
OP here: I didn’t really make any effort to pursue documentation re: the public_name field, and I have really almost no production experience with OCaml whatsoever. I certainly have complaints about OCaml’s tooling, but I can assure you that any argument against it appealing to my authority is certainly flawed.
I wasn’t really appealing to your authority, in fact kind of the opposite. I don’t like using systems that converge to copy-paste magic, and that seems to be what you did, and is likely what I would do. I don’t want to use a weird programming language to configure my project, I want something simple, with happy defaults, that can be understood easily.
I guess I generally prefer convention over configuration in this case, and that doesn’t seem to be what the OCaml community values, which is why I gave up on it. I’m not saying anyone is right or wrong, it’s just not a good fit for me, particularly for hobby projects.
Trying to put this more directly, it sounds like he or she is trying to say: “wake up and get out of your ivory tower”; this post is more so a reaction to the kinds of academic training that new graduates try to apply to their work. I think they usually give up that kind of thinking anyway; they eventually get scoffed at.
I’m all for getting results, but I find this attitude to be too heavy. I know the author is trying to drive home a point, but it’s the case that developers write code, day-to-day, for reasons that only in a circuitous way contribute to their salary. Is that so secondary to getting results that it’s not worth mentioning? Or is it better to keep that a secret?
The post has this weird, business-y subordinate tone to it, as if programming is mostly what you can get away with shipping to production. It is incredibly reductionistic and dull. If our lot in life as programmers is to write glue code to whatever the Internet deems as The Best Library, then I’m done with programming. There is room for a healthy appreciation for making things that work well when stressed and shipping regularly.
I take pride in my work, and that means doing things as best I can. At the end of every day, I at least have the satisfaction of knowing that.
Queuing theory. Retry with exponential back-off and jitter. Is this stuff people pick up from blog posts or are they actually taught in a typical undergraduate CS program?
Pick up from asking friends while struggling with an issue, or from reading papers and blog posts. Or at university. I think the statistics professor gave me a nudge. I’d read the TCP papers at the university library and asked about something after, I think, a lecture about the Poisson distribution, and got a really helpful monologue about skewed distribution of input. (Translated to programmerese: use backoff and make sure you don’t accidentally synchronise.)
There are many good papers. It’s a bit difficult to explain how to recognise the good ones. Reading the morning paper archives and the papers it links to might help.
I once took down pretty much the whole Google App Engine platform, in the middle of a Republican convention that Google/YouTube was sponsoring/covering (yes, that happened), because I didn’t think about that. Whoops.
(Also just terrible project management, ridiculous demands from Google/YouTube to me, the solo developer at the agency they’d outsourced to, and other reasons. But definitely the thing that technically happened is I DDOSed App Engine, and Google paid me to do it)
Yes, I was specifically taught these concepts at my undergraduate CS program in my networking programming course. Graduated in 2019 from Edinboro University of Pennsylvania, which is not an especially well known program. But of high quality!