Hacker News new | ask | show | jobs
by Gabriel439 3170 days ago
Author here: this is written for a primarily functional audience who already take for granted that it's good to minimize effects, but let me try to rephrase it another way for people who don't have that background

Typically there are two ways that our programs can ingest values:

* statically, via imports

* dynamically, via reading values

Why not ingest all values via imported code? If the import system is sufficiently lightweight this is simpler and easier than reading values the traditional way, plus you can read in things that are not plain values (like functions and types)

In that sense, reading/downloading text and parsing it becomes an implementation detail of the compiler and from the programmer's point of view what you're left with is a network of pure functions that can refer to each other across impure boundaries

4 comments

You seem to be just playing with semantics and claiming it offers a new way of thinking about I/O.
I don't want to think about I/O. That's the point

I want to focus on connecting pure code together without thinking about the details of how that happens. I want this for the same reason that I don't want to think about manually allocating registers, managing memory, or caring about evaluation order

Also, like I mentioned, traditional I/O can't import functions or types, so this is strictly more powerful

"Playing with semantics" is a good description of programming language theory.
"Compiling" throws me off a bit, because I usually think of that as a process that bakes in everything except for I/O. It seems like for Dhall, this is done on-demand, like an interpreter, and so the result of execution could change if the contents of the referenced files change. But I'm not certain how this would extend to ahead-of-time compilation. Would you have to mark file references by whether they are to be read at compile-time or run-time?
So you do dynamic compilation? How is that different from including files in PHP or one bash script calling another? I can even do that in .NET languages like C#.
In Dhall's case it is a typed interpreter. So every time you interpret an expression there are three phases:

* Resolve all imports (transitively, if necessary)

* Type-check the code

* Normalize the code (a.k.a. evaluation, but it can normalize functions, too)

It's a little more complex than that (for example, imported code is itself type-checked before substitution into the syntax tree), but that's the basic idea

It's closer in spirit to a Bash script sourcing another bash script (i.e. using "source"). However, the difference from Bash is that:

* In Bash the unit of code composition is statements (as opposed to expressions in Dhall) * When you source another script you can only do so at the top-level of the program, as a statement (as opposed to Dhall where you can import other expressions anywhere within the syntax tree)

I'm less familiar with PHP so perhaps somebody who knows it better can explain how Dhall relates to the PHP model

Look into .NET. You can do the same thing with C# by compiling and loading it dynamically.
That was my question as well. This doesn't seem substantially different than the dynamic include/require/eval functionality in many interpreted languages like Python, Ruby, Perl, PHP, etc.
The primary distinction from a dynamic language is that Dhall is typed, total, and does not permit arbitrary effects (only importing other code is allowed), so it's safe to evaluate arbitrary remote code and it's also safe to use Dhall expressions to configure programs since they consist of nothing but pure functional logic. Dhall is first and foremost a programmable configuration language so safety is one of the primary language requirements
Sounds a bit like safe tcl, a subset of tcl you could embed into your own program for untrusted scripts.

https://www.tcl.tk/software/plugin/safetcl.html

Minus the type safety, functional approach, etc...but similar in purpose.

Yes, that's a good analogy

However, I think the more important thing I'm trying to fix is how we compose code. The Rube-Goldberg machine the post refers to is the complicated mechanisms we have to deal with for combining code fragments. Reading in a value shouldn't be any different from reading in code and shouldn't require any more overhead than just copy-and-pasting a URL into your program

What I don't get is, is your compiler different from a reflective framework that takes functions that receive the world state and update its interface as a consequence of the functions output?