• vosper 7 days ago |
    I like the way the code examples work: A live editor with documentation that shows up on the right hand side (click the Play with Hazel) button.

    But does it any more than a live editor and type checker? Can you actually create a program that does something?

    • disconcision 7 days ago |
      we have ~ another year of basic type system and editor features prior to the 'doing something' phase. there are early-stage feature branches with stuff for web GUI programming & data science applications, but parts are awkward without in-progress basics like implicit polymorphism, a module system, and more sophisticated type inference.
  • aassddffasdf 7 days ago |
    Elm/ML is an interesting choice of mis-mash & a subtle slap in the face of Haskell? (Which on the surface is far more like Elm than ML is).
    • colonwqbang 7 days ago |
      I thought the same thing. "Hazel" sounds like a play on words, a "Hazy Haskell"? Or is it because hazels and elms are trees.
    • noelwelsh 7 days ago |
      My reading is:

      * Elm because Elm focused on making the language pleasant to use, and Hazel is in the same tradition of combining HCI + PL

      * ML because Hazel is a strict / eager language, and people talk of ML family languages, of which Haskell is one.

      So I don't think omitting Haskell is meant to be a slap in the face.

    • aithrowawaycomm 7 days ago |
      This seems like an attempt to stir up a flame war. Hazel is written in ReasonML and uses OCaml-style syntax, and the Elm influence is in the design of an interactive programming environment based on running the program as you edit it. I think they could have said SLIME/ML and conveyed a similar idea. I strongly doubt the authors have anything against Haskell.
    • wyager 7 days ago |
      I would take that to mean "strict evaluation" and "simple type system"
      • disconcision 7 days ago |
        yes. also keen on first class modules (coming soon to Hazel). syntax could have easily been more haskell-like, current team is pretty ecumenical wrt surface syntax, excepting that i have as yet been unable to convert anyone to the gospel of s-expressions.

        biggest reason more haskelley syntax didnt/hasnt happen/ed is the current syntax engine does not support significant indentation. hazel concrete syntax isn't stable yet though we're also thinking seriously about semi-colons

  • dang 7 days ago |
    Related. Others?

    Hazel: A live functional programming environment featuring typed holes - https://news.ycombinator.com/item?id=24299852 - Aug 2020 (14 comments)

    Also:

    Tylr: Demo of tile-based editing, a new kind of structure editing - https://news.ycombinator.com/item?id=27926758 - July 2021 (40 comments)

  • agentultra 7 days ago |
    Haskell has type holes. There are plugins that give you code actions to complete them, split case, etc. I love type holes.

    Agda has them too and they're more powerful there: https://agda.readthedocs.io/en/latest/language/lexical-struc...

    • epolanski 7 days ago |
      Typescript has a hole type too implemented in fp-ts and effect-ts.

      Super useful for when you don't know what are you missing and get a type signature for it.

      It's mostly useful for when you declare some `const foo: (bar: Bar) => Whatever` and in the midst of your implementation you don't know what you're missing.

      Requires an advanced level in TS to be used to the max.

      https://gcanti.github.io/fp-ts/modules/function.ts.html#hole

      https://effect-ts.github.io/effect/effect/Function.ts.html#h...

      • williamdclt 7 days ago |
        I’m struggling to understand the use, do you have a concrete example?
        • dietr1ch 6 days ago |
          Brady has a presentation about Idris where he shows Type-Driven development (where you write code that could typecheck with some holes and you get the compiler to help you figure out the missing types for your "whatever/something" untyped variables)

          https://youtu.be/X36ye-1x_HQ?t=5m15s

          • LandR 6 days ago |
            This is awesome, but I find that syntax so hard to follow, but that's just me being unfamiliar with it I guess.
            • dietr1ch 6 days ago |
              It happens, but all syntax families eventually become familiar, even on LISP you eventually stop worrying about the parentheses as you learn how to grok the code.
        • epolanski 6 days ago |
          Suppose you declare some:

          const transformFoo: (foos: Foo[]) => Bar

          and you start implementing it and get stuck with some callback or whatever you can put a `hole` and it will tell you the type signature of what you're missing.

      • disconcision 7 days ago |
        there's this hacky quick way of partially emulating typed holes sans library too: https://dev.to/gcanti/type-holes-in-typescript-2lck

        we (team hazel) recently used that device in our typescript version of a hazel setup for typed-hole-contextualized code completion as described in https://dl.acm.org/doi/10.1145/3689728

    • cies 7 days ago |
      For me Idris has best type holes.
  • davesnx 7 days ago |
    I always loved hazel, probably a great tool to teach. What has been build with it?
  • mmastrac 7 days ago |
    This is semi-related to one of the killer features of Eclipse that never really made it into any large-scale systems: the ability to run incomplete or broken code. The Eclipse Compiler for Java had a special feature that it could generate bytecode for nearly any file, including those that were utterly broken. It would mostly work, and you could incrementally work on unit tests alongside the code being developed.

    It was honestly one of the most productive environments I ever worked in, and I'm somewhat sad nobody else has implemented this.

    • tomcam 7 days ago |
      I have never heard about this before. What exactly would happen to broken code? For example, would it skip the equivalent of the broken source line, or would it stub out a function altogether or what?
      • gmueckl 7 days ago |
        I only used that feature inadvertently a long, long time ago. As I remember, the program would throw a Throwable exception when it would enter code that wasn't translatable. There was some sort of hot reloading, too. So you could try to fix the code and continue.

        The really neat thing was that the Ecliose Java compiler is built specifically to support the IDE, so all the the warning and error annotations in the editor come from the actual compiler even while you are typing. There is no separate parser and linter just for the editor. I believe that the ability to translate broken source files on a best effort basis is actually an offshoot from that functionality.

        • ryukoposting 6 days ago |
          That sounds like an incredibly useful feature. Do you recall what version you were using?
          • gmueckl 6 days ago |
            I believe that Eclipse 2.x already had most of these features, but it certainly was in almost all 3.x versions as far as I remember. That IDE was amazingly far ahead of its time. Even 20 years later, tools like VS Code feel like a shocking regression in capabilities to me.
            • jakewins 6 days ago |
              Well, regression in that feature set, but it’s better in other features, many of which drove people off of Eclipse.

              When it worked, it was really, really good, agree. My experience was that it usually didn’t though, swap branches a few times and the caches would be broken, time for “invalidate caches and restart”. Multiple times per week, each time it’d take an hour to re-index.. that was a lot of time we got back again when we switched to IntelliJ

            • ryukoposting 6 days ago |
              This sounds like the sort of feature that will show up in a "vintage software" youtube video essay in a few years. I kinda want to go find it and give it a whirl.
        • veqq 6 days ago |
          Common Lisp's REPL works like this.
      • mmastrac 7 days ago |
        Literally that, it would throw exceptions with the compiler error. And as a sibling comment mentioned and I had forgotten -- it would allow for hotpatching code at runtime as you fixed compiler errors.

        You could literally start the skeleton of a webserver and gradually add functionality to it without recompiling and it would mostly "just work". Certain changes would require the app to be restarted.

        • cies 7 days ago |
          CRTL-SHIFT-F9 (IntelliJ) works for me on most Java and Kotlin code in IntelliJ, as long as no method/class signatures are changed.
    • WantonQuantum 7 days ago |
      I use IntelliJ now and I definitely miss this feature of Eclipse.
      • cies 7 days ago |
        Why? What did you miss about it?

        I'm asking as I prefer strict compilers that force me to handle all cases.

        • WantonQuantum 7 days ago |
          Generally, it's a very pragmatic thing like being able to quickly run something to make sure it's working but some other part of the code is temporarily broken because I'm currently changing things and don't care that that part is currently broken. In IntelliJ I have to stop doing what I'm currently thinking about and go over to that other part of the code and comment out some things or otherwise fix it up (usually in a way that won't be permanent because it's broken for a reason) before I can run the code I'm working on.

          In an ideal world, the codebase would be modular and testable and all those good things but I work in a large enterprise dev team and some of the codebase is many (many) years old and it's no longer feasible to refactor it into anything like what would be needed to allow the code to be modularized in such a way that would obviate the necessity to do the above.

        • foota 7 days ago |
          I don't think they want to have thing broken in the steady state, but anything that relies on things building for analysis etc.,. Could benefit from this.
    • fire_lake 7 days ago |
      Isn’t this possible with any untyped language?

      It does sound like a good feature though - very few languages have opt-out type checking. This is much better than opt-in IMO.

      • conartist6 6 days ago |
        Yes and no. You need a universal way of saying "something that should exist here is missing"
      • 7h3kk1d 6 days ago |
        Hazel will also run incomplete programs around holes. Most untyped languages will just crash as soon as something is incomplete.
        • instig007 6 days ago |
          Laziness would be enough for this case, Haskell will happily run your programs around undefined
    • diegs 7 days ago |
      And then you have Go, which won't even let you compile code with an unused variable...
      • Joker_vD 6 days ago |

            func TestWhatever(t *testing.T) {
                // ...lots of code
        
                _, _, _, _, _, _, _ = resp3, resp4, fooBefore, subFoo, bar2, barNew, zap2
            }
        
        Like, I get it, it's a good feature, it caught quite a lot of typos in my code but can I please get an option to turn this checking off e.g. in unit tests? I just want to yank some APIs, look at their behaviour, and tinker a bit with the data.
        • TwentyPosts 6 days ago |
          All of this could've been prevented if Go just had two ways to compile. Debug and release.

          The go devs decided against this since they didn't want to build a highly optimizing (read: slow) compiler, but that is missing the point of developer ergonomics.

          • umanwizard 6 days ago |
            It could be prevented in an even simpler way: emitting warnings.

            Most people nowadays ban building with warnings in CI and allow them during local development. But “CI” was barely a thing when go was developed so they just banned them completely. And now they are probably too stubborn to change.

            • yencabulator 5 days ago |
              The Go decision was explicitly to not have warnings, and the unused identifier thing complained about is merely a consequence of that.

              https://go.dev/doc/faq#unused_variables_and_imports

              • dleary 4 days ago |
                As an outsider to Go, it feels to me like this basic pattern comes up over and over again in Go:

                Q. Why can’t I have feature X?

                A. We thought of that already, and your opinion is wrong.

                Q. But basically every other language in existence supports this. And it makes development easier. We would really, really like it. Please?

                A. Apparently you don’t get it. Here’s a pointer to a 15 year-old post on a mailing list where the first time someone asked for this, we said no. Your opinion is wrong.

              • umanwizard 4 days ago |
                > And now they are probably too stubborn to change.

                Sounds like we agree!

        • umanwizard 6 days ago |
          The good news is if you use Nix or Guix it’s relatively easy to hack your local build of the compiler to demote the unused variables hard error to just a warning.
          • okwhateverdude 6 days ago |
            You know, for some weird reason, it never crossed my mind to hack the Go compiler to let me do things like that. And it's such a great idea.
          • Joker_vD 5 days ago |
            Well, it's about as easy without neither Nix not Guix.
            • umanwizard 4 days ago |
              It's really not. Yes it's possible to figure out how to build and install the Go compiler, but then you have to repeat that process every time you want to upgrade to a new version. With Guix (I assume Nix is similar) you just save the patch somewhere and then run `guix install --with-patches=go=/path/to/patch go` and everything just works (including reapplying the patch on upgrade).
        • politician 6 days ago |
          This example isn't particularly good code. If you've got "lots of code" that names a bunch of variables (e.g. using ':=') that are never referenced AND you have a good reason not to do so (which I doubt: given this context it looks like an incomplete test), then predeclare these 'excess' variables:

             func TestWhatever(t *testing.T) {
                  var resp3, resp4, fooBefore, subFoo, bar2, barNew, zap2 theirType
          
                  // ...lots of code
             }
          
          Alternatively, use '_' where they are being defined:

             // instead of
             resp2, err := doit()
          
             // use
             _, err := doit()
          
          If, and given this context it's likely, you're checking these errors with asserts, then either change the name of the error variable, predeclare the err name (`var err error`), or split it into multiple tests instead of one giant spaghetti super test.

          That said, in a code review, at a minimum, I would probably ask that these variables be checked for nil/default which would completely eliminate this problem.

          • Joker_vD 5 days ago |
            This is not a piece of code I would commit, obviously! It's a piece of code in the middle of being written and re-written (and re-run, a la REPL), and constantly replacing "resp2" with "_" and back again with "resp2" is friction. Go doesn't have REPL but having a TestWhatever(t *testing.T) function is a mostly good enough replacement, except for this one small problem.
    • kreyenborgi 6 days ago |
      Haskell has something like this with -fdefer-type-errors: https://downloads.haskell.org/ghc/latest/docs/users_guide/ex...

          $ cat foo.hs
          something = to be done
          x = x + 1
          wat = x "¯\\_(ツ)_/¯"
          main = print "hi"
          
          
          $ ghc --make -O foo -fdefer-type-errors && echo sucesfuly compoiled && ./foo
          [1 of 1] Compiling Main             ( foo.hs, foo.o )
          
          foo.hs:2:13: warning: [-Wdeferred-out-of-scope-variables]
              Variable not in scope: to :: t1 -> t2 -> t
            |
          2 | something = to be done
            |             ^^
          
          foo.hs:2:16: warning: [-Wdeferred-out-of-scope-variables]
              Variable not in scope: be
            |
          2 | something = to be done
            |                ^^
          
          foo.hs:2:19: warning: [-Wdeferred-out-of-scope-variables]
              Variable not in scope: done
            |
          2 | something = to be done
            |                   ^^^^
          Linking foo ...
          sucesfuly compoiled
          "hi"
          $
      • argiopetech 6 days ago |
        Haskell also has typed holes (with a similar -fdefer-typed-holes) since 7.10. I've described it in slightly more detail here in a previous post in this thread [0].

        Typed holes (but not the defer- option) have been enabled by default for some time now. They're an immediate go-to when scratching my head over types. I prefer them to the type error output, not only because they give better suggestions, but also because they can be named (_a, _conversionFunction, etc).

        [0] https://news.ycombinator.com/item?id=42016584

    • spockz 6 days ago |
      Agda (2) has a similar feature called holes. Very similar to Haskell’s `nothing` and Scala’s `???`. The difference is that because of the dependently typedness the compiler can sometimes even fill in the code for you based on symbols in scope.
      • argiopetech 6 days ago |
        Haskell has also had typed holes for several major versions now. Any underscore or name beginning with an underscore (to include values and types, unsure about kinds) gets an informative error message describing the type of the name, e.g.:

          Found hole `_' with type f (Free f b)
        
        and relevant bindings, if applicable:

          Relevant bindings include
            >>= :: Free f a -> (a -> Free f b) -> Free f b
              (bound at holes.hs:28:3)
            f :: f (Free f a) (bound at holes.hs:29:8)
            g :: a -> Free f b (bound at holes.hs:29:14)
          In the first argument of `Free', namely `_'
        
        Very useful for working your way out of a situation where the specific incantation to get to the right type isn't obvious.

        Examples from [0].

        [0] https://wiki.haskell.org/GHC/Typed_holes

        • jimbokun 6 days ago |
          Could this be used to build an editor for Haskell like the one for Hazel?
    • ellis0n 6 days ago |
      ACPUL works well even with partially broken code keeping programs free from crashes and freezes. Some functions were broken for a long time, but this didn’t block progress allowing me to complete 90% of important features and fix them after 10 years. This has been verified over time in practice. I believe even 30-50% of a program can work opening up many new possibilities.
    • agumonkey 6 days ago |
      embedded agile mode
  • disconcision 7 days ago |
    happy to answer hazel questions; ive been working on hazel as cyrus' phd student for the last four years, and am currently working on moldable projectional interfaces for live programming in hazel. here are some of the things ive added to hazel: https://github.com/hazelgrove/hazel/pulls?q=is%3Apr+author%3...

    and here's me speaking last week about using typed holes and the hazel language server to help provide code context for LLM code completion: https://www.youtube.com/watch?v=-DYe8Fi78sg&t=12707s

    • jakewins 6 days ago |
      This is probably naive but: How does this differ from something like “declare a type, implement it with methods that all throw NotImplementedException”?

      As in, is this “just” a less boilerplate-heavy version of that, or is it more capable?

      • 7h3kk1d 6 days ago |
        You can play with it at https://hazel.org/build/dev/ but programs don't "crash" when they're incomplete so "1 + 5 + ?" will evaluate to "6 + ?" in the editor. So your program can evaluate as far as possible with the holes. If you're using Java and throw NotImplementedException you lose all context to what did work.
    • riffraff 6 days ago |
      Congrats, this seems fun and neat!

      But small question related to https://hazel.org/build/dev/, given

      > Non-empty holes are the red boxes around type errors

      ... why is the case statement in the list example red-boxed?

      • nrabulinski 6 days ago |
        (Haven’t worked with hazel and I couldn’t find much in the documentation so this may be wrong)

        Because that case is non-exhaustive. It will match a list with 0, 1, or 2 elements, but the last arm matches a list with exactly 2 elements, not 2 or more, so as soon as you get to 3 or more elements, there’s no code to execute.

      • 7h3kk1d 6 days ago |
        If you put the cursor on it you'll see an error message at the bottom. In this case the case expression is inexhaustive because it's only handling lists of size 0, 1, and 2.
    • conartist6 6 days ago |
      Nice to make your acquaintance! I've spent the last four years working on similar tech, though I'm not affiliated with any school or company. I've gone over to the Hazel implementation many times for inspiration and just to check in on the progress.

      Here are some of the biggest questions I have:

      Do you have any plans to bring editor gaps to languages other than Hazel?

      Why is the Hazel editor first a text editor? E.g. it seems 100% happy to let a single poorly judged keystroke create an unbalanced brace or quote pair when it has much more semantically correct options for the next state it could generate...

      P.S. Feel free to come check out BABLR: https://github.com/bablr-lang/, https://discord.gg/NfMNyYN6cX

      • disconcision 6 days ago |
        good questions! both will be addressed soon with david moon's new tylr version (tylr being the underlying syntactic engine for hazel). the new tylr is designed to take a grammar as a parameter; we have a javascript grammar and a partial rust grammar, and are planning editor integrations. the new model also eschews the backpack (the yellow thing that contains matching delimiters) in lieu of inserting missing delimiters as 'ghosts' in a way that always shows the exact parse that the semantics engine is using, but also doesn't prevent typing normally. the current backpack solution is the result of trying to balance natural text editing with mandated syntactic correctness and it definitely has proved to have some rough edges... more on the new system soon
        • conartist6 5 days ago |
          The homepage assures me that Hazel's mission is to take semantic editing and ensure that the core of the experience is text editing in the most literal sense, for example by allowing you to make selections that cross-cut the tree. I just don't understand why!!! Both the current UX and the proposed UX are less useful and less semantic than the editing tools I already use.

          For example in VSCode if I type ( the editor inserts () -- it's actually not a text edit in the sense that the code I produced doesn't map 1:1 to the keys I pressed. No, what actually happened there was already a semantic edit. It was quick and efficient. One keypress. Having a busted document is a worse experience than that, and having a document which is in a sort-of-busted-ghost-mode is also a worse, less semantic experience than I already have. Why would I want either of those experiences for myself or others?

          • disconcision 5 days ago |
            re: the first: this is in contrast to traditional structured editors which actively prevent you from doing edits that break the tree structure, even if the next edit would immediately restore them. users tend to do these often when refactoring; we looked at some examples versus a trad structured editor (MPS) here: https://hazel.org/papers/teen-tylr-vlhcc2023.pdf

            in general though i have mixed feeling about making structured editing more text-like; the above is just about trying to patch a hole in existing structured editors, which doesn't in-itself improve on text. i think we can do better than that; we're exploring more radical directions in a separate project. but i do think it is interesting to see how close to regular text entry we can stay while always maintaining a well-formed (though incomplete) parse state which we can use to constantly run type-checking and evaluation.

            the current model partially succeeds in the above, but at some significant usability cost, including the fact the the backpack obscures what the actual underlying parse state is. the ghost model i'm describing can basically be thought of a generalization of the vscode parentheses insertion you describe; it just works for every multi-delimiter form (eg when you insert 'let' you get ghosts '=' and 'in', with appropriate holes inserted). the utility is (A) the same as an incremental parser in a language server (you get semantic feedback in every state), but because of the ghost/hole insertions it's crystal clear what the parse state is that you're getting feedback from. but yeah the current version doesn't live up to that standard

            • conartist6 5 days ago |
              If you have holes, I would think you should generally be able to make arbitrary structural edits without ever passing through any outright invalid states, not unlike taking Lego bricks apart and reassembling them in a different configuration.

              Recovering from bad parses is the state of the art in the industry right now, but it suffers from the "garbage in garbage out" problem because the user's intent is lost. In point of fact it is never captured in the first place! When you say that typing `let` ghosts out should all that other stuff, that's based on pure assumption. The state of the editor would look just the same if the user was part way through typing out the word `letter` which they intended to use as an identifier. No technology in the world can make things right after the user's intent has been lost.

              The real way forward, the direction nobody is looking, is how to make tools that are more like musical instruments. An instrument doesn't guess at your intention and then aim to please you, but rather it amplifies the importance of each decision and impulse that go into playing it, making the player more expressive then they could have been otherwise.

  • sheepscreek 6 days ago |
    Just came here to say that the editor UI is beautiful, works really well - even on mobile. Color me impressed.
  • hoistbypetard 6 days ago |
    My first thought was the Mac app that's been around for about 18 years now:

    https://www.noodlesoft.com

    And it had a release today.

    https://www.noodlesoft.com/release_notes

    Seems rough to jump on a name that's been in continuous use for that long. Would it be hard to add another word to make it easier to disambiguate?

    • nozzlegear 6 days ago |
      My first thought when viewing your link was the name "Hazel" which has been around since the late 19th century or so. Couldn't the devs have chosen a different name or added another word?

      Jokes aside, name collisions are bound to happen. These two apps seem entirely unrelated so I doubt anyone will accidentally install "Hazel, the Mac app for organizing folders and files" when they meant to use "Hazel, the live functional programming environment organized around type holes."

  • keeganpoppen 6 days ago |
    this seems like it's likely inspired by Idris, to which i say: awesome!
  • boogerlad 6 days ago |
    How does this compare to lamdu?
  • ineil 6 days ago |
    So if I’m told I have an A type personality and am also often called an A hole does that mean I can concatenate both to create the compound condition of having type holes?
  • virtualritz 6 days ago |
    I tried the playground on my Android phone and none of the key presses get through to the source code.

    I can position the cursor by tapping and I get a virtual keyboard but I can't type anything.

    Is this a bug or am I just missing something because If terrible UX?

    • disconcision 5 days ago |
      its a bug. we should be clearer that we dont 'officially' support mobile yet (in that no-one regularly tests with it) but the no keyboard insertion thing is a chrome-specific issue (it works on firefox but there are other issues there)
  • jbjohns 6 days ago |
    Interesting. I didn't notice any reference to Idris [1] but that was the first place I saw this style of development.

    [1] https://www.youtube.com/watch?v=mOtKD7ml0NU

  • jlkuester7 6 days ago |
    Not sure if I have just spent too much time in the JS/TS world and so I have forgotten the pain in this area in proper compiled languages, but to me it seems like needing "typed holes" smells like maybe there is some abstraction missing in your codebase.

    I prefer to have code layered in a way that my inflection points happen across well defined interfaces. Then I can make changes one layer at a time in increments that are small enough to still be able to reason about. But maybe I am totally mising the point of typed holes!

    • 7h3kk1d 6 days ago |
      I'm not sure I understand your point. Typed holes aren't trying to get rid of the concept of interfaces or well designed abstractions. Rather they aim to help deal with incomplete programs that are still under development.
    • mrkeen 6 days ago |
      > maybe there is some abstraction missing

      A hole is the answer to this question. You ask the compiler "what abstraction is missing?" and it tells you.

  • imglorp 6 days ago |
    Interesting syntax: all the "let" bindings end with "in", eg

        let comparison =
          (0 == 0, 0 < 1, 1 <= 1, 2 > 1, 1 >= 1) 
        in
    
    Anyone know why "in" keyword?
    • moomin 6 days ago |
      The bindings are only valid for the expression following in.

      Haskell does the same thing.

    • arthurbrown 6 days ago |
      this is the syntax for variable binding in ocaml.

      Hazel appears to be written in ocaml and mentions being "ml-like" on the site