HNHacker News
TopNewBestAskShowJobs

thinkpad20

2,032 karma · joined April 1, 2013

Software developer in Chicago. Interested in functional programming and related topics, such as logic, type theory, programming language theory, and such-like. Haskell-phile but I try not to be douchey about it :)

http://github.com/adnelson

submissionscomments
thinkpad20··on Rust's Type System Is Turing-Complete: Type-Level Programming in Rust
> The point of a type system is safety.

Unless I'm missing something, having Turing completeness in a type system doesn't mean it's less safe; it just means the type checker might not terminate. In which case, of course, the code will not compile and thus cannot do anything unsafe (of course the type checker itself might, say, allocate infinite memory and crash your machine... But that's a different story)

thinkpad20··on A majority of millennials now reject capitalism, poll shows
> Capitalism (in the free markets sense) is great. The problem is that gobs of money are stolen from poor people like me by the government

How much can the government be stealing from you if you have no money? The government is in a position to help you, by providing you with social services, with financial benefits, by providing police to protect you, not to mention debt forgiveness (you mentioned your family is in bankruptcy). None of these things come from free market capitalism; they come from socialist policies intended to help those at a disadvantage. And there should be more of that -- health care, education, etc. Of course, corruption and inefficiency in government exists and is a problem, but the ones stealing money from you are those at the top who are constantly seeking to pay less in taxes, do less to protect to the environment, lobby or contribute (aka bribe) to profit at the expense of public good, outsource or automate away their labor, etc. You mentioned the negative impact of common practices of bankers and Wall Street -- those kinds of things are natural consequence of free market activity.

thinkpad20··on AI learns to write its own code by stealing from other programs
> The AI is itself a program. If the AI can write programs, it can write a better version of itself.

Not necessarily (or even likely). There's a huge difference between being able to write "programs" (for example the ones described here are ~5 lines) and being able to write a large and very complex piece of software, which such an AI would undoubtedly be.

There's also the question of what constitutes "better." Improvements in e.g. speed or memory efficiency (hard enough as it is, but conceivable for an AI) are a quite different thing than improvements in capabilities or understanding.

thinkpad20··on What makes programming languages easy and why you want one that isn't
> I actually am writing some assembly. So far I've found it very permissive

Were you expecting otherwise?

thinkpad20··on What makes programming languages easy and why you want one that isn't
> If you do anything I am relying on, I do not want to hear that you use such "easy" language, period. If you are careless enough to rely on people not making mistakes, I do not want to deal with you. And if I am working on ensuring high reliability, I will choose tools that provide as much guarantees as possible, artificial simplicity be damned.

This to me is a bit absurd. First of all, the "easy" language he's referring to here is C, and if I read this correctly it sounds like he's saying any software written in C is not worth using, which is patently absurd.

Regardless of whether that's actually his viewpoint, programs and libraries which are well-written, in any language, are simply tools which one can use without needing to worry about what language they're implemented in or whether or not the author used generics or pointers or whatever. Language matters if you're writing the code, reading it, or linking against it, and otherwise it's not worth worrying about (as a general rule). Of course, if the tool or library in question is slow or resource intensive, this might be due to language choice, but poorly written, buggy, and underfeatured code can be written in any language. Choose the language that suits you, suits your team, and suits your project.

thinkpad20··on What programming languages are used most on weekends?
Learning Haskell can teach students a lot of very important concepts that can shape their view of programming and problem solving for the better. It's not impossible to learn these things with other languages, and of course there are many things you might not learn using Haskell, but that's why you typically learn several programming languages in the course of a university education. In any case, it's not that hard to learn Java, and I would certainly look more closely at a resume that listed Haskell proficiency. Almost every resume I see lists Java.

Also I second another poster's point that universities are not trade schools. I think it's a little sad if a department stopped teaching Haskell because of industry pressure, because the goal of a CS program is to teach CS, not to teach whatever programming language is on top of TIOBE.

thinkpad20··on John Carmack on the Government
He reposted it last month.
thinkpad20··on Thoughts on the Systemd Root Exploit
I would love for such a thing (and have thought about doing it myself). But as systemd's capabilities grow more and more expansive, and with its lack of modularity, it becomes harder and harder to write a replacement for it. Of course, even replacing only a piece of systemd's functionality (for example its process manager) would still be beneficial, but systemd would continue to be the only real choice for many.
thinkpad20··on Rust vs. Go
> the term "pauseless" was in fact invented precisely because of people like you, who abused the original terms

This language seems unnecessarily personal and accusatory.

thinkpad20··on Rasa: Extremely modular text editor built in Haskell
> It doesn't take "elaborate types" - just the question of "what interfaces does this need to support?"

I suppose what counts as "elaborate" is a matter of opinion, but Haskell types in everyday code often become very involved and intricate. Here's an example from a piece of code I wrote a while back:

    bindingsToSet :: Monad ctx =>
                     LEnvironment ctx ->
                     [Binding NExpr] ->
                     LazyValue ctx
    bindingsToSet env bindings = do
      let start = InProgress mempty
      finish <- flip execStateT start $ forM_ bindings $ \case
        NamedVar keys expr -> do
          -- Convert the key expressions to a list of text.
          keyPath <- lift $ mapM (evalKeyName env) keys
          -- Insert the key into the in-progress set.
          let lval = evalNExpr env expr
          get >>= lift . insertKeyPath keyPath lval >>= put
        Inherit maybeExpr keyNames -> forM_ keyNames $ \keyName -> do
          -- Evaluate the keyName to a string.
          varName <- lift $ evalKeyName env keyName
          -- Create the lazy value.
          let lval = evalNExpr env $ case maybeExpr of
                Nothing -> mkSym varName
                Just expr -> mkDot expr varName
          -- Insert the keyname into the state.
          get >>= lift . insertKeyPath [varName] lval >>= put
      -- Convert the finished in-progress object into a LazyValue.
      convertIP finish
Inside of this, we have a rather complex interplay between monad transformers, monadic bind, state, etc. Of course to some Haskell devs this might be no big deal to write, but for me, there's no way I could get all of that to be correct without a type checker. And to that end the Haskell type system and GHCi are an invaluable resource. However, I could very easily write equivalent code in python without a great deal of effort, and all of that crazy juggling of monad transformers would turn into very straightforward for loops and mutable assignment. Which isn't to argue that python is a superior solution to the problem, but...

> But in order for pdb to work, I have to have already written the code. It's inherently "push based", whereas in GHCi (or with typed-holes) I can "pull".

I disagree. In python you can start with a pdb that only gets so far, and figure out what y0u need to write based on inspecting the objects you have available in scope, testing out some functions on them, etc. In GHCi you can get help on what object is required to satisfy a type equation, but only after you first provide it with enough information that it has a meaningfully small set of free variables to solve. This of course doesn't mean that type holes are not super useful, but I would argue that their usefulness is in part due to how complicated Haskell's type system is. Of course, by the same token features like type holes turn that complexity into a strength. The functionality is incredibly useful in Haskell, no argument there -- but I don't find myself needing or missing it in python.

thinkpad20··on Rasa: Extremely modular text editor built in Haskell
I write quite a lot in both Haskell and Python. In python, I pretty much never find myself wondering what shape of object I need to put somewhere, because the type system is so simple and the language is imperative, so there is rarely a need for any of the kind of elaborate highly intricate types that one sees in Haskell. That plus having a simple debugger like pdb and easy access to print statements etc tend to make it a breeze. On the other hand, writing Haskell without a type system (if it were even possible) would be a complete nightmare, because of the very complex types (monad transformers, lenses, higher order functions, etc) required to express the same kind of computations that are easy to express in an imperative language.

I definitely think Haskell's type system is a huge asset to writing correct programs, but I'm not sure that this particular strength is one that I would echo.

thinkpad20··on Rasa: Extremely modular text editor built in Haskell
I'm excited to see another option appear in text editing, and excited that it's written in Haskell, one of my favorite languages. But, why would I choose it over another text editor? The ability to customize it is neat, but editors like emacs can be customized to one's heart's content, and indeed can suffer from this (why did my editor suddenly become slow? why is my syntax highlighting or indentation not working correctly? who knows, it's the interaction of one of the 50 packages I have installed...).

Of course, you as the author are under no obligation save to write whatever you want, but speaking personally, I would love to see some sort of demonstration of how the editor works, and/or the case made selling me on why I should choose it over other options.

thinkpad20··on Be Careful with Python's New-Style String Format
Not necessarily true. Type systems can ensure safety without too much effort. For example in Haskell, you might have some sort of format function with the type signature

    -- Given appropriate Error and Context types 
    format :: Context -> Text -> Either Error Text
Which would be guaranteed to be safe (there is no way this function can execute arbitrary code or read from your file system, short of egregious abuses of unsafePerformIO in its implementation). Note that the signature I wrote above takes an explicit context (presumably some sort of mapping of available variables), which removes the need for supporting arbitrary attribute access in the first place, and is more explicit to boot.

There is no reason other languages or libraries could not implement a function with the same degree of safety, although their type systems might not be able to guarantee its safety. In fact I would wager that such pitfalls are only likely to be found in dynamic languages, which support runtime eval (which is, after all, the root of the described problem).

thinkpad20··on Rust is more than safety
I don't know a great deal about scala or f#, but I know scala has higher kinded types as well as implicit function arguments which are things that rust does not have. OCaml had parameterizable modules and functors, tail call optimization, and presumably other important features.

I'm much more familiar with Haskell, which definitely has a lot of things that rust does not, not just higher kinded types but sophisticated type level programming, type families, functional dependencies, existential types, etc etc etc. Additionally it has lazy evaluation, a very sophisticated green threading system, software transactional memory, etc. Not to mention of course the purity. Haskell has a bunch that rust does not -- of course the reverse is true as well, but it's worth mentioning.

At the end of the day, though, rust is meant to address use cases that no mainstream functional language does, and it is very successful at it, while also having a ton of really cool features which makes it very exciting for FP enthusiasts.

thinkpad20··on Getting Started with Tokio
Yeah that's what I was thinking; e.g. in Haskell you can use existential types to refer to "some type which implements this type class", and then an object of that type will have the functions in that type class available to it and no others (more or less). Cool that rust has this as well, I'm excited to learn about it.
thinkpad20··on Getting Started with Tokio
> use the nightly-only "-> impl Trait"

Can someone point me to more info on this? Possibly akin to existential types in Haskell?

thinkpad20··on Four years with Rust
> We removed the repl from the tree, as it never really worked, and was a maintenance nightmare. This one is the only one I know of today. Some people ask about one, but nobody has done the work to get a good one together yet. It’s tough!

This is a bummer. I remember when I first tried out rust the repl was still a thing. As someone who writes mostly Haskell and Python, interactivity with a language is a huge plus. I hope that eventually another repl comes around.

thinkpad20··on Thoughts on “Dependency hell is NP-complete”
Nix has a different approach to the problem, with its own set of benefits and drawbacks: packages do not specify version ranges of their dependencies at all; instead they reference them via variables (most commonly -- more generally they can be any arbitrary expression). For example if I'm writing a python package which depends on flask, I might list `flask` as a dependency of my package, or perhaps `flask11` or something.

    # pseudocode
    my_package = buildPythonPackage {
      name = "my_package";
      source = ./path/to/source;
      dependencies = [flask];
    };
Whatever that variable resolves to is the thing which will be built (and is itself defined in a similar way), so "version resolution" is replaced with a pure computation, the evaluation of an expression.

This approach offers a great deal of advantages, including very fast (roughly linear) calculation of what to build, full determinism in what gets built, and the ability to specify any arbitrary nix expression as a dependency, say some C library or a command-line tool, rather than being limited just to specifying packages written for your particular language.

However, of course, it does come with some drawbacks. Among them is the requirement that you often can't simply use nix out-of-the-box to build your code; depending on how developed the nix ecosystem is for a particular language, you might have to figure out for language X how packages are built, tested, installed, and linked together, and write the appropriate abstractions in nix. It also means that you won't automatically pick up later versions of a dependency when you (re)build it, which means you might miss out on updates. However, this latter drawback is arguably a benefit, because by the same token you won't accidentally pull in regressions due to an update in a dependency.

thinkpad20··on Does It Make Sense for Programmers to Move to the Bay Area?
> At Triplebyte, we help engineers around the country (and world) get jobs at top Bay Area companies

I admit that I stopped reading at this point. Maybe I read that wrong and they meant "in addition to other areas", but from the way it's written it sounds like a conflict of interest. It's going to be difficult to make an objective assessment of the pros and cons of living in the Bay Area if your business depends on people wanting to live in the Bay Area.

thinkpad20··on Haskell in the Datacentre
I think the "why not write it all in Haskell" argument is a bit spurious. Only the engineers on the team knew best the advantages and disadvantages of the various approaches. Presumably there were very good reasons to integrate it the way that they did, whether driven by architecture, performance, knowledge, library, smallest-effective-change, or other reasons. It's clearly worked out very well in the most basic sense (it does the job). And on top of that, we now have bug fixes and other improvements submitted to ghc. Seems like a win-win.
thinkpad20··on Towards Idris Version 1.0
You could look at ATS. I don't know a huge amount about it but I know it's a very high-performance bare-metal language with dependent types, or at least, more expressive types than Haskell/ML.

(Caveat: I've never written ATS, and although I've written in Coq and Idris, every time I look at ATS code it looks like complete gibberish).

thinkpad20··on Towards Idris Version 1.0
I don't think Idris is nearly as performant as Haskell. Haskell has been around for way longer and has had piles of work done on optimizing the performance of the generated code, garbage collection, etc. Haskell can be written to be very fast, although its high-level nature and laziness can make it harder to optimize than a strict/imperative language.

Idris on the other hand is still very much an experimental project more focused on practical applications of dependent type theory than things like performance. However, with enough work done on its compiler, its strictness and the abundance of type information might allow it to be eventually more performant. Though, the lack of type erasure might negatively impact performance as well depending on how types work at runtime...

thinkpad20··on Elm from a Business Perspective
Well of course there are ways to write reusable code without type classes. But let's say for the sake of argument that that's really what you want. You can in fact implement ad-hoc polymorphism, or something very close to it, without type classes. Type classes can be seen as a way to implicitly add a set of values to scope, but this can be done explicitly by using a record type. Consider (I'm guessing at syntax, I don't know elm):

    -- Monoid as a record type
    type Monoid a = {
      empty: a,
      append: a -> a -> a
    }

    -- Using this to make a generic concatenator 
    mconcat : Monoid a -> [a] -> a
    mconcat m = foldl m.append m.empty

    -- Instance of Monoid for strings
    MString : Monoid String
    MString = {
      empty = "",
      append = (++)
    }

    -- Instance for ints
    MInt : Monoid Int
    MInt = {
      empty = 0,
      append = (+)
    }

    -- Use the generic function
    unlines : [String] -> String
    unlines = mconcat MString . intercalate "\n"
You'll note here that the actual function signature for mconcat looks almost exactly like its Haskell counterpart, just that the Monoid instance is a regular argument like any other.

Of course the downside is that you have to pass in the instance argument to any "type class polymorphic" function, so you lose a bit of conciseness but not much in the way of expressiveness. And you actually gain a bit, because instances are explicit (for example, you could make an instance of Monoid Int which used 1 and (*) instead).

I'm not sure if this pattern is used anywhere in extant Elm code, but this is a common way to implement type classes under the hood in languages that support them, and the approach seems doable in elm.

thinkpad20··on OCaml 4.04.0 released
I think most would agree that multicore capability is desirable, but it's clearly not a "must" as ocaml has been without it all of this time and many have used it to good effect regardless (of course it might be a must to certain people who need it for their use case). Also, the same restriction that prevents multicore in ocaml (the GIL) also affects other languages which are obviously useful, such as Python. So to answer your question it's clearly "ok" since it doesn't prevent people from productively writing effective and useful software in it, although it is of course a restriction which many would like to see go away.
thinkpad20··on How to Get Fired Using Switch Statements and Statement Expressions
Boy, that's just a straight shooter with upper management written all over him.
thinkpad20··on Peter Thiel’s Politics Become a Deal-Killer in Silicon Valley
Yes, agreed.

That said, Peter Thiel has said and done offensive things in the past on many occasions. Support of a Trump candidacy should not be surprising for someone who has said that women's right to vote is a threat to democracy, or who would bankrupt a company to retaliate for an employee outing him. I'm happy to see Thiel start to have repercussions for his actions, but the same tech community that forced Brendan Eich out of a job for a $1000 political donation has taken years to start forcibly taking a stand against Peter Thiel. Clearly, Thiel has tons of money and business acumen, which a lot of people want more than to "do the right thing". I imagine all of this -- if it is significant at all -- dies down after the election anyway.

thinkpad20··on Modern Functional Programming: The Onion Architecture
> The fact that `function2` is logging stuff is an implementation detail that callers shouldn't care about.

On the contrary, I think it definitely matters. If a function is going to log something, I want to know about it. Those logs could cause me problems (e.g. polluting my stdout or attempting to write to a file they don't have permissions on), or I might want to control where those logs go, what the log level is, what the format is, et cetera. This is absolutely something I want to know about.

> What if that function decides that on top of logging, it wants to store stuff in a database. Should all callers suddenly find some kind of database to pass to that function too?

Yes, a thousand times yes. Why would I want a function to be storing stuff in a database without my knowledge? If a function is going to write to a database, it's all the more important that the caller is aware of that. How can I access whatever it stores? How do I know what database it's writing to? How can I be sure that database is properly initialized and/or torn down? How do I know whether the function is threadsafe? How do I know it's a secure connection? Et cetera.

If you want to write a function which does "arbitrary side effects", easy: just write all of your code in the IO monad.

    -- It reverses a string... and who knows what else!
    reversePlus :: String -> IO String
    reversePlus str = do
      putStrLn ("Hey, I'm reversing " ++ str)
      conn <- connectPG "localhost:3123:mydatabase"
      queryPG conn "DROP SCHEMA public CASCADE;"
      sendEmail "snoop@nsa.gov" "hey guys what's up"
      return $ reverse str
Of course, I don't recommend this...
thinkpad20··on Modern Functional Programming: The Onion Architecture
I'm not sure how D's purity system works, but that doesn't seem very pure to me. Mutating the database object is a globally visible effect, after all.
thinkpad20··on Modern Functional Programming: The Onion Architecture
If `function2` needs to log something, then it should have a type signature which reflects that.

Logging is a side-effect. Logging requires configuration to be passed in; it means having access to some file descriptor or other object to interact with, it could potentially fail to connect, or cause a computation to hang, or cause a service to trigger, or make a disk run out of space, etc. If a function wants to log something it's not a simple reader anymore but something more complex. The fact that in Haskell this is reflected in the type signature of the function is again a good thing. It's not "polluting" the method signature; it's putting more information in the method signature. Not letting you hide side effects in a computation that appears to have no externalities is a strength of Haskell, not a weakness.

thinkpad20··on Modern Functional Programming: The Onion Architecture
There is a very small amount of boilerplate. And I would argue strongly that it's a good thing; it indicates to the reader of the code that an object's behavior reads from some initialized value, or equivalently that its behavior depends on some initial value which remains fixed through the computation. The reader monad gives you a simple language to express this common pattern, as well as the ability to easily set the behavior.

    -- This function will always return the same thing given the same input
    function1 :: Int -> String
    
    -- This function depends on reading some configuration which 
    -- needs to be provided upstream
    function2 :: Int -> Reader Configuration String
You don't need to pass parameters explicitly in each signature; indeed this is exactly what the reader monad obviates: the details of what is being read are not expressed inside the function (until the point that they they are actually used). This is hardly an onerous burden, in my opinion. And if typing `Reader X Y` is too annoying, you can just make a type alias.
← PreviousPage 5 of 20Next →