frontpage.
newsnewestaskshowjobs

Made with ♥ by @iamnishanth

Open Source @Github

fp.

France's homegrown open source online office suite

https://github.com/suitenumerique
207•nar001•2h ago•110 comments

Start all of your commands with a comma (2009)

https://rhodesmill.org/brandon/2009/commands-with-comma/
374•theblazehen•2d ago•134 comments

Hoot: Scheme on WebAssembly

https://www.spritely.institute/hoot/
65•AlexeyBrin•3h ago•12 comments

Reinforcement Learning from Human Feedback

https://arxiv.org/abs/2504.12501
40•onurkanbkrc•3h ago•2 comments

OpenCiv3: Open-source, cross-platform reimagining of Civilization III

https://openciv3.org/
749•klaussilveira•18h ago•234 comments

Coding agents have replaced every framework I used

https://blog.alaindichiappari.dev/p/software-engineering-is-back
108•alainrk•2h ago•117 comments

The Waymo World Model

https://waymo.com/blog/2026/02/the-waymo-world-model-a-new-frontier-for-autonomous-driving-simula...
1002•xnx•23h ago•569 comments

Show HN: One-click AI employee with its own cloud desktop

https://cloudbot-ai.com
7•fainir•1h ago•2 comments

First Proof

https://arxiv.org/abs/2602.05192
11•samasblack•32m ago•5 comments

Stories from 25 Years of Software Development

https://susam.net/twenty-five-years-of-computing.html
6•vinhnx•1h ago•1 comments

Vocal Guide – belt sing without killing yourself

https://jesperordrup.github.io/vocal-guide/
132•jesperordrup•8h ago•55 comments

Unseen Footage of Atari Battlezone Arcade Cabinet Production

https://arcadeblogger.com/2026/02/02/unseen-footage-of-atari-battlezone-cabinet-production/
91•videotopia•4d ago•20 comments

Ga68, a GNU Algol 68 Compiler

https://fosdem.org/2026/schedule/event/PEXRTN-ga68-intro/
30•matt_d•4d ago•6 comments

Making geo joins faster with H3 indexes

https://floedb.ai/blog/how-we-made-geo-joins-400-faster-with-h3-indexes
148•matheusalmeida•2d ago•40 comments

Reputation Scores for GitHub Accounts

https://shkspr.mobi/blog/2026/02/reputation-scores-for-github-accounts/
6•edent•2h ago•0 comments

Show HN: Look Ma, No Linux: Shell, App Installer, Vi, Cc on ESP32-S3 / BreezyBox

https://github.com/valdanylchuk/breezydemo
253•isitcontent•18h ago•27 comments

Monty: A minimal, secure Python interpreter written in Rust for use by AI

https://github.com/pydantic/monty
266•dmpetrov•18h ago•142 comments

A Fresh Look at IBM 3270 Information Display System

https://www.rs-online.com/designspark/a-fresh-look-at-ibm-3270-information-display-system
6•rbanffy•3d ago•0 comments

Show HN: Kappal – CLI to Run Docker Compose YML on Kubernetes for Local Dev

https://github.com/sandys/kappal
10•sandGorgon•2d ago•2 comments

Hackers (1995) Animated Experience

https://hackers-1995.vercel.app/
531•todsacerdoti•1d ago•258 comments

Sheldon Brown's Bicycle Technical Info

https://www.sheldonbrown.com/
409•ostacke•1d ago•105 comments

Show HN: I spent 4 years building a UI design tool with only the features I use

https://vecti.com
353•vecti•20h ago•159 comments

Show HN: If you lose your memory, how to regain access to your computer?

https://eljojo.github.io/rememory/
321•eljojo•21h ago•198 comments

An Update on Heroku

https://www.heroku.com/blog/an-update-on-heroku/
448•lstoll•1d ago•296 comments

What Is Ruliology?

https://writings.stephenwolfram.com/2026/01/what-is-ruliology/
54•helloplanets•4d ago•54 comments

Cross-Region MSK Replication: K2K vs. MirrorMaker2

https://medium.com/lensesio/cross-region-msk-replication-a-comprehensive-performance-comparison-o...
6•andmarios•4d ago•1 comments

Microsoft open-sources LiteBox, a security-focused library OS

https://github.com/microsoft/litebox
365•aktau•1d ago•190 comments

How to effectively write quality code with AI

https://heidenstedt.org/posts/2026/how-to-effectively-write-quality-code-with-ai/
292•i5heu•21h ago•246 comments

Dark Alley Mathematics

https://blog.szczepan.org/blog/three-points/
103•quibono•5d ago•30 comments

Female Asian Elephant Calf Born at the Smithsonian National Zoo

https://www.si.edu/newsdesk/releases/female-asian-elephant-calf-born-smithsonians-national-zoo-an...
53•gmays•13h ago•22 comments
Open in hackernews

When if is just a function

https://ryelang.org/blog/posts/if-as-function-blogpost-working-on-it_ver1/
113•soheilpro•3mo ago

Comments

blahgeek•3mo ago
Seems lispy
taeric•3mo ago
Without the discussion of applicative-order versus normal-order.
middayc•3mo ago
BTW: In Lisps, if is still a special form / macro, because in Lisp lists are evaluated by default, in Rebols if can be a function because blocks (lists) aren't evaluated by default.
kazinator•3mo ago
You can rarely successfully generalize about languages in the Lisp family. :)

TXR Lisp: (relevant to this article) there is an iff function that takes functional arguments.

Square the odd values in 0 to 9:

  1> (mapcar [iff oddp square use] 0..10)
  (0 1 2 9 4 25 6 49 8 81)
The use function is a synonym of identity: i.e. just use the incoming value as-is

Square the even ones instead by inverting oddp with notf:

  2> (mapcar [iff [notf oddp] square use] 0..10)
  (0 1 4 3 16 5 36 7 64 9)
Get rid of use with iffi: a two-argument iff with an implicit identity else:

  3> (mapcar [iffi oddp square] 0..10)
  (0 1 2 9 4 25 6 49 8 81)
Now about the point about Lisps and if: the regular if operator with value and expression arguments has a companion if function:

  4> (special-operator-p 'if)
  t
  5> (fboundp 'if)
  t
Unlike in some other dialects like Common Lisp, a symbol can have a binding in the macro or operator space, and in the function space at the same time.

But this if is not useful control. It's useful for things like being able to map over a function-like facsimile of the if operator. E.g. take an element of the (a b c d) or (x y z w) list depending on whether the leftmost list has a nil or t:

  6> [mapcar if '(nil t nil t) '(a b c d) '(x y z w)]
  (x b z d)
In the reverse direction, being able to write a macro for a function function exists, allows for ordinary macros to be "compiler macros" in the Common Lisp sense: provide optimizations for certain invocations of a function.

This dialect is not even "weird"; overall it is Common-Lisp-like. Right down to the multiple namespaces, which is why the [...] syntax exists for referring to functions and variables in a combined virtual namespace.

veqq•3mo ago
What's the benefit of being implemented as lisp-2 but acting like lisp-1 with [ ]? Why not just be a lisp-1?

> TXR is an original notation for matching entire text documents or streams, inspired by the unification that underlies logic programming systems

This has me hooked.

kazinator•3mo ago
while there are certain simplicities and economies in a Lisp-1, it's a bad idea on the whole, and has irksome disadvantages.

Lisp-2 also has irksome disadvantages, like the verbosity in code working with functional arguments.

I want to give myself and my users the advantages of Lisp-1 and Lisp-2, as well as ways to avoid their respective disadvantages, so there is no way to get around having some kind of combination that lets us work in different styles.

bloaf•3mo ago
I checked the Rye homepage, and it has "dialects" which allow more familiar math infix notation. In that sense it is very tcl-y, tcl technically being a lisp.

Looking at their rather confusing looping mechanisms, they probably could benefit from being a little more tcl-y, since tcl has some of the best looping semantics I've worked with.

middayc•3mo ago
Any concrete feedback on looping mechanisms being confusing is appreciated.
bloaf•3mo ago
Certainly:

I can tell the rye devs like the idea of everything being a function. But in their very first "language basics" section, they introduce assignment not as a function call, but as some kind of magic that happens when you have colons in a name.

So when we get to the "looping" section, it is the first time we have seen a colon-having-name outside the context of assignment:

> loop 3 { ::i , prns i }

And it is explained that the above line of code is "injecting" values for the code block to "pick up".

But right away this begs a number of questions:

* Why the double-colon? I would assume each loop-body-evaluation happens in its own scope, and that we're not creating global variables, so a single colon (:i) should be sufficient, right?

* What are we doing, conceptually? Is the ::i meant to be "a function which when given a value modifies its enclosing scope to include the symbol i" or "an unassigned symbol which the loop function will use to do something akin to term-rewriting with?"

* Do we really need a symbol at all, or could we just have a the point-free loop "loop 3 {prns}"?

* If we can't have the point free thing, is it because somehow the injected value would end up "to the left" of prns, if so, why would we want that?

* If we're doing something more like term rewriting, why isn't the symbol given as a separate argument from the body?

middayc•3mo ago
Rye is foremost a REBOL and REBOL has this notion of many types of words at it's core:

`word` - regular word, can evaluate to value it's bound to or call a funtion if bound to a function

`word: "value"` - set-word, assignment that you noticed

`probe :word` - get-word, always returns bound value, doesn't call a function if it's bound to a function, in Rye this is `?word`, because `:word` is left-set-word.

`'word` - literal word, evaluates to a word itself

etc ...

Rye adds even more word types. Rye also has left to right flow so it adds left-set-word. In Rye all assigned words with set-words are constants and they are used by default. So we also need a "mod-word", that is the double colon that you noticed, and left-mod-word. Because of left-to-right flow Rye also has .op-words and |pipe-words.

The logic around words, op-words and pipe-words ... I tried to explain here:

https://ryelang.org/meet_rye/specifics/opwords/

Another pattern you noticed (good observation btw:) is the idea of injected blocks that isn't used just for loops, but also for conditionals, function bodies, HOF-like functions etc ...

https://ryelang.org/meet_rye/specifics/injected_blocks/

All in all it is supposed to be a compact set of ideas that fit together. Some are somewhat unusual.

bloaf•3mo ago
> Rye also has left to right flow so it adds left-set-word. In Rye all assigned words with set-words are constants and they are used by default. So we also need a "mod-word", that is the double colon that you noticed, and left-mod-word

So I would assume that the :i is actually constant within the loop body scope. That is, the loop function is doing something like this:

; i is not assigned in this scope

evaluate {1 :i, prns i}

evaluate {2 :i, prns i}

evaluate {3 :i, prns i}

; i is still not assigned in this scope

But it sounds like you're telling me that :i would actually escape the scope of the loop body and so it needs to be modifiable or else the loop will break.

middayc•3mo ago
Yes, Rye follows REBOL in this case. Plain block invocation doesn't create it's own scope / context. That holds for do, if, either, loop, for, map, etc.

It would be costly to have this on by default. If you want separation there are many ways to achieve it. Rye has many functions related to contexts / scopes. For creating contexts in multiple ways and evaluating code inside contexts or with context as parent or isolated context, etc.

And a lot of builtins directly accept anonymous functions in place of blocks of code.

For example for loop also accepts function if you want separation and don't mind the cost.

    for { 1 2 3 } fn { x } { print x }
    ; which can also be written with fn1
    for { 1 2 3 } fn1 { .print }
    ; or latest experiment where we have syntax for 3 injected blocks 
    ; .() - same as "with"
    ; .[] - same as "reduce/with"
    ; .{} - same as "fn1"
    ; where it's already decided department from REBOL: 
    ; () is "do"
    ; [] is "vals"
    ; {} is literal block 
    for { 1 2 3 } .{ .print }
kazinator•3mo ago
If you treat assignment as a function, then you have to reify environments as run-time objects, whereby you basically lose lexical scope.

Lisp originally, as in LISP, had assignment as a function: it was called SET.

To use it, you usually had to quote: (SET 'VAR 42).

It worked without an environment parameter because variables were in a pervasive environment, but the quote was needed to get the variable symbol as a run-time value. (SET VAR 42) would mean evaluate VAR to a symbol, and then pass that symbol to the SET function along with 42, so whatever variable was in VAR would be assigned.

Assignment is inherently non-functional, since it is a side-effect, so it is mostly counterproductive to model it as a function.

A pattern matching or logical language can have implicit bindings as the results of an operation, and so produce variables that way. Then instead of assignment you have shadowing, in that some construct binds a variable again that was already used, so that the most recent value then emerges, shadowing the previous one.

bloaf•3mo ago
So tcl handles it somewhat more elegantly, I think. It also has a set function, but does not require any special quoting because it uses the $ prefix to denote "get the value of this symbol":

    set a 5
    puts $a  #prints 5
and of course because it is modeled as a function (albeit an impure one) you can pass arguments to it as normal:

    set a b
    set $a 5  #equivalent to set b 5
    puts $b   #prints 5
of course, everything in tcl is a string, so this works too lol

    set a 5
    set $a b
    puts $5  #prints b
middayc•3mo ago
I’ve personally always thought that REBOL’s use of set-words and similar constructs was a strength. It makes sense conceptually, is visually distinguishable, and maintains a strong sense of internal consistency.

REBOL (and by extension, Rye) was never designed around the idea that everything must be a function. It just turns out that this approach fits naturally within the core principles and rules of the language.

All “active” words happen to be functions, because nothing else is needed. The behavior of different word types (and, more broadly, value types) is determined by the evaluator. In that sense, you could say that Rye does have syntax, expressed through its distinct word types.

veqq•3mo ago
Combinatory programing offers functional control flow. (Here is a straight forward explanation: https://blog.zdsmith.com/series/combinatory-programming.html ) I was inspired to write `better-cond` in Janet:

    (defn better-cond
    [& pairs]
    (fn [& arg]
        (label result
            (defn argy [f] (if (> (length arg) 0) (f ;arg) (f arg))) # naming is hard
            (each [pred body] (partition 2 pairs)
                (when (argy pred)
                (return result (if (function? body)
                                    (argy body) # calls body on args
                                    body)))))))
    Most Lisps have `cond` like this:

    (def x 5)
    (cond
    ((odd? x) "odd") ; note wrapping around each test-result pair
    ((even? x) "even"))

    Clojure (and children Fennel and Janet) don't require wrapping the pairs:

    (def x 5)
    (cond
    (odd? x) "odd"
    (even? x) "even")

    My combinatoresque `better-cond` doesn't require a variable at all and is simply a function call which you can `map` over etc.:

    ((better-cond
    (fn [x] (> 3 x)) "not a number" # just showing that it can accept other structures
    odd?   "odd"
    even?  "even") 5)

    Of course, it can work over multiple variables too and have cool function output:

    (defn recombine # 3 train in APL or ϕ combinator
    [f g h]
    (fn (& x) (f (g ;x) (h ;x))))

    (((better-cond
    |(function? (constant ;$&))
    |($ array + -)) recombine) 1 2) # |( ) is Janet's short function syntax with $ as vars
roxolotl•3mo ago
Do you have any recommendations for a language where you _have to_ use these concepts. I love playing with them but I find that unless i’m paying a lot of attention in most cases I fall back to a less functional style even in a language like Janet. I’d love to find a language where you largely have to use these combinatorial logic style functions so I can’t just default back to other styles.
veqq•3mo ago
J and BQN (APL has off-ramps...)

https://code.jsoftware.com/wiki/Essays/Tacit_Expressions

https://mlochbaum.github.io/BQN/doc/tacit.html and https://mlochbaum.github.io/BQN/doc/control.html

Forth, Factor and Uiua (which combines the above approach) don't use these concepts yet are also inherently point-free, and without lambdas so you definitely wouldn't be able to rely on functional techniques!

codery•3mo ago
This looks great, I'm still learning Janet and couldn't write it myself.

I only know about: https://github.com/Engelberg/better-cond in clojure which is different it adds syntax enhancement + control flow convenience.

Similar better-cond can be written in clojure too:

  (defn better-cond [& clauses]
    (fn [& args]
      (some (fn [[pred result]]
              (when (apply pred args)
                (if (fn? result)
                  (apply result args)
                  result)))
            (partition 2 clauses))))
But it's not composable as Janet's version, it will fail when mapped over, because it may return a plain value instead of a callable one. In Janet, all values can naturally participate in higher-order contexts due to its uniform treatment of callables, while in Clojure, only actual functions can be composed or mapped.
icepat•3mo ago
Is this just a quirk in my display, or are all the code blocks in this formatted like a CIA black highlighter
middayc•3mo ago
I checked in Firefox and Chrome (on Linux) and code samples look OK to me. What browser/OS are you using. Maybe send me a screenshot at janko dot itm at gmail.
blauditore•3mo ago
Same here. I guess it's an issue with (system) dark theme (you can simulate that in dev tools. Android here, so must be Chrome.
middayc•3mo ago
I fixed it thanks! I was able to activate dark more in Firefox on desktop and find problems with CSS.
icepat•3mo ago
This is on FF for me. Specifically Zen, which is FF based.
middayc•3mo ago
You still have this problem? I tested and fixed css yesterday. What exactly is problematic?
icepat•3mo ago
I hadn't checked since I posted this, but today the entire document appears properly unredacted! Nice work.
singlow•3mo ago
It's only a problem if I have my browser set to use dark theme or system theme and my system theme is dark if I switch it to light theme. Everything looks good. So most likely he's using some kind of CSS framework that's automatically responding to the dark theme, but other styles that he's hand coded are not compatible with it
middayc•3mo ago
Thank you all for heads up! I was playing with CSS and didn't test the dark mode. I think it's fixed now.
jrochkind1•3mo ago
Smalltalk, anyone? I guess the OO version.
sebastianconcpt•3mo ago
Yeah, here. They should know the feeling of booleans as instances and ifTrue: ifFalse: as methods. But for us is such an obvious thing that isn't really something too remarkable. It normalized language awesomeness.
middayc•3mo ago
There is a language https://sprylang.se/index.html which started more as Rebol and moved towards Smalltalk.
ivanjermakov•3mo ago
The drawback is that this approach elevates code blocks to first class. It means that there is a semantical difference between a value that is a block and a value that is a result of a block. This reduces code clarity, because now block def/result is discriminated by context instead of syntax.

- closures get tricky, i.e. having outer scoped variables within a block

- inter-block operators still need special care, e.g. return should return from a function or a nearest block, same for break/continue/etc.

danlitt•3mo ago
This criticism seems at face value to also apply to first-class functions, which I thought was a totally uncontroversial pattern. Do you dislike those too?
ivanjermakov•3mo ago
First-class functions are problematic too[1], but function is always a definition. While code block is usually meant to be executed right away.

[1]: https://github.com/ziglang/zig/issues/1048

em-bee•3mo ago
can you please explain what the actual problem here is? i am trying to read through that issue discussion, but i am not quite getting what makes first-class functions problematic. as far as i am concerned, not having first-class functions would be a serious limitation to a language that would make me avoid using that language for anything serious.
hatthew•3mo ago
This is interesting, but I'm not convinced it's better than the python it's being compared to. Memorizing and understanding the behavior of functions that perform control flow seems no easier than memorizing and understanding hardcoded syntax/keywords. The additional flexibility of making everything a first-class citizen allows people to write code that is too clever for its own good. I could be wrong but I think there is a broad consensus that reflection is a Bad Idea.

Open to being convinced otherwise

(tangent but related, aren't the "Loops" and "Iteration" examples given for python literally the exact same syntax, with the exception of changing how the iterable is generated?)

Nevermark•3mo ago
> I could be wrong but I think there is a broad consensus that reflection is a Bad Idea.

Reflection may be bad in practice for other reasons/conditions, but the lack of simple/minimal/regular primitive conventions in many languages, makes reflection a basket of baddies.

The code blocks of Rye seem comparable to closures, which is a sensible thing to have. Once all code blocks are closures, there are fewer concepts to wrangle, and functional control makes excellent sense.

hatthew•3mo ago
That makes sense, thanks!
hshdhdhehd•3mo ago
I agree. In any somewhat functional language (I.e. all the mainstream ones) you can wrap "if" in a function if you please.

E.g.

    function funif (b, f) {
       return (b && f())
    }

If you want to do clever stuff. I never feel the need as I would rather abstract over bigger things.
middayc•3mo ago
You can do it, but that is not how the (default) control structures work in those languages. There is usually also some syntax cost.
hshdhdhehd•3mo ago
Thats a good point. Idiomatics are important and not following them makes incompatible code.
andriamanitra•3mo ago
You may not want a fresh scope for control flow as you often want to use variables from the outer scope inside the if statement. Imagine you wanted to do something like this with your if statement implemented with a function (this is how the syntax would look like using a block argument in Ruby):

    state = "inactive"
    if_func(condition) {
        state = "active"
        activate_button.disabled = true
        deactivate_button.disabled = false
    }
In many languages you would need to wrap `state` in something that can be passed by reference, and make the function take multiple parameters. For example in JavaScript it would turn into something like this mess:

    let state = ["inactive"];
    if_func(condition, ({state, activate_button, deactivate_button}) => {
        state[0] = "active";
        activate_button.disabled = true;
        deactivate_button.disabled = false;
    }, {state, activate_button, deactivate_button});
8n4vidtmkvmk•3mo ago
I think that part could use some improvement. References vs values should have different syntax, like C++. I think the interpreted languages and Java botched this.
middayc•3mo ago
It depends on what you want. If you want the most stabile and predictable way to specify the behavior, then static control structures have little downsides.

If you want to explore with how you can specify behaviors or rules and create new options or the ones tightly fitting your problem domain or mental model, then this gives you more tools to do so.

davidw•3mo ago
Seems a bit like Tcl, which lets you create your own control structures like that.
middayc•3mo ago
I don't know a lot about Tcl, but one thing I know is said for it "everything is a string". In REBOL's it's somewaht reverse as all this live code are REBOL (Rye) values and REBOL (and Rye) have an unusual number of datatypes, REBOL 30+ (many literal types), which it uses as additional information for functions to work with, and is usefull at creating dialects

For example file-path, url and email address are distinct types in REBOL where in mosta languages are just strings.

pwg•3mo ago
Or redefine the language provided 'if' statement, in the case that one wanted to do so.
middayc•3mo ago
You can't really redefine if because everything is a constant, but you can define if in your own context yes.
cmacleod4•3mo ago
In Tcl you can redefine "if", or even delete it entirely if you're crazy enough :-)
middayc•3mo ago
You can't delete word binding in Rye. I will have to add some solution just for REPL, because sometimes you instantiate something and make a bug and need to reiterate, but in the language itself no.
spankalee•3mo ago
I wish they showed the `else` syntax, because the traditional ALGOL-style if-then-else statement doesn't look native when shoved into most function call notations, unless you have something pretty interesting around named parameters and expressions delimiters.
middayc•3mo ago
there is no if { } else { } in REBOL or Rye and it wouldn't really fit. There is either function that accepts two code blocks. It can act as a typical if / else or as a ternary expression as it also returns the result of a block:

    print either pwd = "correct" { "Hello" } { "Locked" }
This is Rebol's doc on either, Rye's works exactly the same: https://www.rebol.com/docs/words/weither.html
iamevn•3mo ago
See the `either` function further down

  either some-condition { print "was true" } { print "was false" }
analog31•3mo ago
Interestingly, Excel provides "if" as a function:

=if(condition, value-if-true, value-if-false)

middayc•3mo ago
Yes, Io language is closest to that syntax probably:

https://iolanguage.org/guide/guide.html#Introduction

analog31•3mo ago
I wonder if it's more readable to non-programmers than control flow.
danlitt•3mo ago
That's because Excel is a pure functional language :-)

https://www.youtube.com/watch?v=0yKf8TrLUOw

lisbbb•3mo ago
This discussion makes me so happy because people still care about programming languages and not just on stupid Java or whatever is making gobs of money. LISP should have a much larger following than it does, though I fully admit it has its own warts.
solomonb•3mo ago
Given an algebraic data type such as:

    data List a = Nil | Cons a (List a)
You can define its recursion principle by building a higher-order function that receives an element of your type and, for each constructor, receives a function that takes all the parameters of that constructor (with any recursive parameters replaced by `r`) and returns `r`.

For `List` this becomes:

   foldr :: (() -> r) -> (a -> r -> r) -> List a -> r
The eliminator for `Nil` can be simplified to `r` as `() -> r` is isomorphic to `r`:

   foldr :: r -> (a -> r -> r) -> List a -> r
   foldr z f Nil = z
   foldr z f (List a xs) = f a (foldr f z xs)
For `Bool`:

    data Bool = True | False
We get:

    bool :: a -> a -> Bool -> a
    bool p q True = q
    bool p q False = p
Which is precisely an If statement as a function!

:D

mappum•3mo ago
I own the 'if' package on npm, which I wrote to be functions that can replace the if keyword, making no use of the if keyword in its definition.
middayc•3mo ago
interesting. Give us an example of it's usage ... or how you implemented it?
morcus•3mo ago
I looked at the code - it uses ternary expressions.
ozy•3mo ago
Useless unless the logical operators receive their rhs unevaluated. And that is generalized as a language feature.
sparkie•3mo ago
A general language feature would be fexprs, or call-by-name (which can be combined with call-by-value using call-by-push-value).

In Kernel[1] for example, where operatives are an improved fexpr.

    ($define! $if
        ($vau (condition if-true if-false) env
            ($cond 
                ((eval condition env) (eval if-true env))
                (#t (eval if-false env)))))
$vau is similar to $lambda, except it doesn't implicitly evaluate its operands, and it implicitly receives it's caller's dynamic environment as a first class value which gets bound to env.

$lambda is not actually a builtin in Kernel, but wrap is, which constructs an applicative by wrapping an operative.

    ($define! $lambda
        ($vau (args . body) env
            (wrap (eval (list* $vau args #ignore body) env))))
All functions have an underlying operative which can be extracted with unwrap.

[1]:https://ftp.cs.wpi.edu/pub/techreports/pdf/05-07.pdf

tromp•3mo ago
For lambda calculus, the motto is "when everything is a function". The boolean true is the function λx.λy.x, while false is λx.λy.y. If b then x else y then simply becomes b x y. In a functional language like Haskell that is basically a typed lambda calculus with lots of syntactic sugar, we can replicate this with:

    type MyBool a = a -> a -> a

    myTrue :: MyBool a
    myTrue = \x y -> x
    myFalse :: MyBool a
    myFalse = \x y -> y

    myIf :: MyBool a -> a -> a -> a
    myIf b myThen myElse = b myThen myElse

    main = print $ myIf myTrue "true" "false"
Y_Y•3mo ago

  yourIf == myId
tromp•3mo ago

    No instance for (Eq (MyBool a0 -> a0 -> a0 -> a0))
        arising from a use of ‘==’
Undecidability of function equality aside, we could indeed define "myIf = id" instead.
Y_Y•3mo ago
Fair point, though if I remeber correctly there is only one function with type a->a and so we get equality automatically.

(This may be untrue in the presence of the likes of unsafeCoerce.)

tromp•3mo ago
There are other functions MyBool a -> MyBool a, such as not = \b x y -> b y x.
nextaccountic•3mo ago
This is https://en.wikipedia.org/wiki/Church_encoding#Church_Boolean...

But there are other encodings

tromp•3mo ago
This is both the Church encoding and the Scott encoding of the abstract data type

    data Bool = True | False
making it pretty much the only encoding you find in the literature.

This is quite different from the case of the natural numbers, where not only do the Church and Scott encoding differ, but there are several other reasonable representations fitting particular purposes.

motorest•3mo ago
I'm definitely missing some key insight because after reading the article I felt it was a strawman argument.

Python already has conditional expressions, which already allow 'x if (predicate) else y'. Therefore in Python if is already equivalent to a function, and is composable.

Once you realize this, and also understand that Python has logical operators that can short-circuit, all Python examples feel convoluted and required the blogger to go way out of it's way to write nonidiomatic Python. If the goal was to make a point with Python, why not write Python?

middayc•3mo ago
Python was used as an example because people know Python and reader can compare it to something known.

I wasn't trying to make Python look awkward. I was trying to write equivalent Python, you are very much welcome to suggest improvements to my Python examples.

Other languages could be used instead of Python, for example Javascript. But I feel Python is more contained language, with clear ways to do thing (I guess I don't know them that well :) )

motorest•3mo ago
> Python was used as an example because people know Python and reader can compare it to something known.

I don't think you understood the point I made.

My point is that Python supports conditional expressions for years.

Support for conditional expressions already means that in Python indeed "if is just a function".

Therefore, the whole premise of the article is null and void, and the article is thus pointless.

This is evident to anyone who is familiar with Python. If you have any experience with Python and you are familiar with idiomatic Python and basic features such as list comprehension, you are already widely aware how "if is just a function".

> I wasn't trying to make Python look awkward. I was trying to write equivalent Python (...)

Except you didn't. You failed to even acknowledge that Python already supports "if is just a function" with basic aspects such as list comprehension.

This basic aspect of Python is covered quite prominently in intros to Python, but somehow you failed to even acknowledge it exists, and proceeded to base your argument on a patently false claim.

middayc•3mo ago
If Python has special syntax for `if` and so makes if an expression, it doesn't mean that `if` is a function.

If `if` is a function why don't you call it like other functions in Python?

    if( .... )
Functions are first class in Python now AFAIK. Can you assign `if` to a variable?

Also, blog-posts is not just about `if`, is `for` a function in Python, `def`, `return`, `class`?

motorest•3mo ago
> If `if` is a function why don't you call it like other functions in Python?

The whole point is that yes, you can call it like a function, because Python supports it as an expression.

I seriously recommend you take the time to go through a Python tutoria.

> Also, blog-posts is not just about `if`, is `for` a function in Python, `def`, `return`, `class`?

Python supports list comprehension, so yes it is also about `for`.

Once you discover that Python supports conditional expressions and list comprehension, do you honestly believe the blog post has any merit? The blog post even shows a failure to do basic homework to verify what Python actually supports and not supports.

correct_horse•3mo ago
Just here to point out that the actor’s name is Bob Odenkirk not Odendirk. In a statically typed language this would be an error at compile time not hacker news comment time.
middayc•3mo ago
My bad :), Gilligan typed language would resolve this at compile time. Will fix.
andrepd•3mo ago
Bravo, Vince.
matt123456789•3mo ago
gcc() { curl -d '{"title": "Does this code look right?", "body": "$(cat $1)"' https://$HN_BASE/api/ask }

export gcc

emoII•3mo ago
Interesting that this article makes no mention of eager vs lazy evaluation - isn’t a big reason that if, for etc has to be special forms in an eagerly evaluated language that their arguments need to be lazily evaluated, which of course, deviates from the rule? Also, lazy evaluation is achieved in an eagerly evaluated language as simply wrapping a block of code in a function, which makes lazy evaluation isomorphic with the contents of the article
conradludgate•3mo ago
> You might wonder: “Won’t the block execute immediately when passed as an argument?” Here’s the key insight: in Rye, code blocks { ... } are values. They don’t evaluate until you explicitly tell them to.
emoII•3mo ago
You're correct, that is lazy evaluation. The entire article talks about lazy evaluation without mentioning it, which was my point
middayc•3mo ago
I've been programming in REBOL for decade(s) so this is just how it worked and made sense and we never used term "lazy evaluation", so it not part of my vocabulary when explaining this.

Blocks are not evaluated by default, but they are eagerly evaluated if the function that accepts it decides to do so (if, do, loop) ... I understand lazy evaluation more like something that is meant to be evaluated, but physically only gets evaluated when or if you do need the result, which I'm not sure is entirely the same.

vanderZwan•3mo ago
Lazy evaluation typically is a language-wide calling convention choice right? With that in mind I'm guessing the first-class block aspect of Rye might make it feel too loaded of a term to use to the author, because Rye's blocks-as-values are more flexible than just that. But maybe one mention of "this is kind of like lazy evaluation" would help.
the_gipsy•3mo ago
When everything is an expression, like rust (I think), that's enough. Most of the control flow are used as statements, but if needed, they can be used directly as expression, and you get basically all the advantages. After seeing how effective this is, using a language that doesn't have this feature can be really annoying.

On the other hand, I would like to explore "when arithmetics is just a function". I think Elm does this well: operators are just functions with two arguments that can be written as "1 + 2", the familiar way, or "(+) 1 2". Then you can compose it like "map ((+) 2)" (currying) so you get a function that adds 2 to every item of a list, and so on.

middayc•3mo ago
This is another subject but mathematical operators are and behave (for better and worse - there are also negative consequences of this consistency) like all other op-words, they just don't need . in front and correct, non-op-word is prepended by _.

    12 + 23    ; is technically
    12 ._+ 23  ; or not using op-word is
    _+ 12 23   ; or if we bind function _+ to add
    add: ?_+   ; we get
    12 .add 23 ; and
    add 12 23
It has downside because op-words don't behave exactly as math expressions would, but you can use parenthesis and we have a math dialect which includes full math precedence rules.

    math { 12 * 2 + 23 * 3 }
foofoo12•3mo ago
The Trade-offs section doesn't list the biggest one.

Secretly, all code wants to be spaghetti. You and your team have to put a conscious effort into prevent that from happening. Degrading the core of the language like this is like inoculating your homebrew with sewage and expecting it not to go wrong.

middayc•3mo ago
That is sort of like saying all visual art projects want to become "the million dollar / pixel homepage" so providing an empty canvas and full color palete will just enable people to create visual sewage because nothing stops them from doing so.

I never programmed in a team, so my experience of programming is probably very different from yours. You probably want something like electric cattle fencing (if I borrow your juicy language) for your team, but if I program for my self I just want an open field of Rye I can explore :)

diegoperini•3mo ago
That analogy may not be suitable for this case because value proposition between the aesthetics vs the function is different for visual art projects compared to software. There is also the maintainability factor where most aged software (especially the closed source ones in private sector) change maintainers every few years. Old maintainers most often lose access to the source code and become unreachable after leaving their job.
middayc•3mo ago
True, there is the "old maintainer" aspect, that differs.

But what exactly does this mean in relation that what are special forms in other languages are function calls in Rye?

Is the problem that you somebody could make their own control-structure-like functions? All these function calls have exactly the same evaluation rules, which is not something you can say about "special forms" in "normal" languages because special forms are exactly rules that break the regular evaluation rules.

Rye already has a lot of control-structure-like functions in it's standard library and many other functions that accept blocks of code and aren't related to control-structures. Yes, a "stupid" person can write "stupid" code in Rye, but you don't need much flexibility in any language to write stupid code.

I fully admit there are languages that are more suited for teams, and languages that are more suited for solo developers, for this reason.

That was also my point mentioning "million pixel website" because that "art" is directionless and crowdsourced and a painter can use the same pixels or even more flexible options to create beautiful images.

foofoo12•3mo ago
By all means, go full avant-garde. I have nothing against experimental stuff like this. The "throw it at the wall an see what sticks" idea.

According to my own experience, it's entirely possible to write a rancid spaghetti carbonara all by yourself. I'm not saying you shouldn't do it (it's a heck of a learning experience) or it should be banned or prevented or anything. But if the language comes with a tin of e. coli, at least list the side effects.

middayc•3mo ago
What exactly would be the e. coli here?

I mean this is not the first dynamic, homoiconic, reflective language. Which one of these os something I didn't list is the obvious negative or e. coli as you call it?

veqq•3mo ago
> degrading the core

> experimental

These ideas have been tried and tested for 60 years now and result in less spaghetti.

middayc•3mo ago
Yes, a lot of REBOL ideas, that Rye took, or various functional, homoiconic langauges implement (haskell, lips, clojure, scheme, Io, Factor) come from search for greater internal consistency than your ALGOL-derived status-quo languages provide. :)

So degrading the core doesn't make much sense if you are not more specific. This is the core.

padjo•3mo ago
The apply* ?thing is kinda hard to parse, would turn me off seeing lots of that in a codebase.
middayc•3mo ago
I agree. This is not something you would usually use to program, at least not all three together, but as it's written it's consistent and signals exactly what it does, to someone that knows Rye conventions / rules.

I can break it down for you, but yes ... it's quite specific, I was trying to apply an `if` which is not something I needed to do or would look to do so far. The point is that you can also apply all "control structure like" functions, like any other function - consistency, not that this is advised or often used.

?word is a get word. `x: inc 10` evaluates inc function, so x is 11, but `x: ?inc` returns the inc function, so x is the builtin function.

apply is a function that applies a function to a block of arguments. It's usefull when you want to be creative, but it's not really used in run of the mill code.

.apply (op-word of apply) takes first argument from the left. `?print .apply [ "Hello" ]`

Here we needed to take second argument from the left and this is what a * modifier at the end of the op- or pipe-word does. `[ "hello" ] .apply* ?print`

You asked for it :P

6gvONxR4sf7o•3mo ago
There's more to this that I'd absolutely love to see in a language, and I can't tell if rye supports. If you want an ergonomic `if` you need its scope aspects too.

Consider this example

    <beginning of the function>
    x = ...
    if foo:
       y = ...
    else:
       x = ...
       y = ...
    <rest of the function>
Critical parts of the ergonomics are that

a) in each branch, we have everything in scope that comes from <beginning of the function>

b) in <rest of the function>, we have everything in scope that was assigned or reassigned in the executed branch

I'd love a language that supports programmable stuff like if, since I'm tired of python autodiff not handling `if` and `for`. But it would really need programmable scope stuff to still allow the ergonomic "scope effects" that make `if` and `for` blocks ergonomic.

middayc•3mo ago
Sorry, but I don't understand the example. In Rye, as in REBOL all first level block evaluations (do, if, for, loop, ...) happend in current scope (context).

Rye has first class contexts and it's one of more exciting things to me about it, but I'm not sure it's related to what you describe above. More on it here:

https://ryelang.org/meet_rye/specifics/context/

One thing that pops out at the example above is that I wouldn't want to define a y inside if block, because after the if you have y defined or not defined at all, at least to my understanding of your code.

Rye is constant by default, most things don't change and you need less veriables because it's expression based and it has left to right "flow" option. So you only make variables where you really need them, which is less common and specific, it should be obvious why you need a variable.

6gvONxR4sf7o•3mo ago
10 days later... sorry, I didn't see your comment.

The example I gave had a few pieces:

- x is defined prior to the if/else, and overwritten in just one branch - y is defined in both branches

So in the rest of the function, we have both x and y available, regardless of which branch is taken.

I just took a quick read of the context page and the context basics page, but it's still unclear to me whether you can program how scopes/contexts interact in rye.

In my example, we I'd say we have a few different scopes worth mentioning, and I'm curious how programmably we can make them interact in rye:

Scope 1. Right below the first x = ...: we have names available form <beginning of the function> and have x available as the ... stuff. Presumably the `foo` in `if foo` lives in this scope.

Scope 2T. Right after the true branch's y, we have scope 1 plus y introduced

Scope 2F. Right after the false branch's x and y, we have scope 1 plus x "pointing to" something new and y introduced.

Scope 3. Below the if/else, where <rest of the function> lives. This is either scope 2T or scope 2F. x is either scope 1's x or scope 2F's x, and y is either scope 2T's y or 2F's y.

In the original articles language,

So the scope relationships in an if/else are a diamond DAG taking the names from before it, making them available in each branch's scopes, and then making a sorta disjoint union of the branch's names available afterwards. Could that be programmed in rye, to allow the kinds of naming ergonomics in my previous example, but with the if/else being programmable in the sense of the original article? I'm especially interested in whether we could overload it in the traditional autodiff sense.

Responding to a different part of your comment about using names rarely in rye, I've found that I benefit a ton from handing out names more than most people do in functional languages, just for clarity and more self-documenting code. Like, the ide can say "`apples` is broken" instead of "error in location xyz" and I can rebuild my mental state better too when revisiting code.

Cosi1125•3mo ago
In R:

    > `if` = function(a, b) { print(a); print(b) }
    > if (2 < 5) { 6 }
    [1] TRUE
    [1] 6
(Rye also looks like a nice language, though!)
middayc•3mo ago
I used R for a while to do visualizations, but I never got to it's more homoiconic side, but I saw since then that it has it.

Can you also put code into { 6 }, like { print(6) }?

Does this overwrite R's if or how does the scoping in R work?

Cosi1125•3mo ago
The `6` is the code, I just put the first thing that came to my mind there :D In my example, if you replace it with a more sophisticated code block, R will evaluate it and print out the result of the evaluation.

Typically, you'd want to parse the unevaluated code, though:

    > `if` = function(a, b) { print(substitute(a)); print(substitute(b)) }
    > if (foo) { print("bar") }
    foo
    {
        print("bar")
    }
It overwrites `if`, in the current scope, of course.
skybrian•3mo ago
Using closures or code blocks when they're unnecessary gets in the way of static analysis and (sometimes) readability. It's often better to avoid using language constructs that are more powerful than needed for the job. You can still use them when doing something more advanced.