[HN Gopher] When if is just a function
___________________________________________________________________
When if is just a function
Author : soheilpro
Score : 97 points
Date : 2025-10-14 14:20 UTC (4 days ago)
(HTM) web link (ryelang.org)
(TXT) w3m dump (ryelang.org)
| blahgeek wrote:
| Seems lispy
| taeric wrote:
| Without the discussion of applicative-order versus normal-
| order.
| middayc wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| Any concrete feedback on looping mechanisms being confusing
| is appreciated.
| bloaf wrote:
| 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 wrote:
| 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 wrote:
| > 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 wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| 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 ph 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 wrote:
| 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 wrote:
| 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!
| icepat wrote:
| Is this just a quirk in my display, or are all the code blocks in
| this formatted like a CIA black highlighter
| middayc wrote:
| 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 wrote:
| 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 wrote:
| I fixed it thanks! I was able to activate dark more in
| Firefox on desktop and find problems with CSS.
| icepat wrote:
| This is on FF for me. Specifically Zen, which is FF based.
| middayc wrote:
| You still have this problem? I tested and fixed css
| yesterday. What exactly is problematic?
| icepat wrote:
| I hadn't checked since I posted this, but today the
| entire document appears properly unredacted! Nice work.
| singlow wrote:
| 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 wrote:
| 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 wrote:
| Smalltalk, anyone? I guess the OO version.
| sebastianconcpt wrote:
| 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 wrote:
| There is a language https://sprylang.se/index.html which
| started more as Rebol and moved towards Smalltalk.
| ivanjermakov wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| > 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 wrote:
| That makes sense, thanks!
| hshdhdhehd wrote:
| 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 wrote:
| 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 wrote:
| Thats a good point. Idiomatics are important and not
| following them makes incompatible code.
| andriamanitra wrote:
| 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});
| middayc wrote:
| 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 wrote:
| Seems a bit like Tcl, which lets you create your own control
| structures like that.
| middayc wrote:
| 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 wrote:
| Or redefine the language provided 'if' statement, in the case
| that one wanted to do so.
| middayc wrote:
| You can't really redefine if because everything is a
| constant, but you can define if in your own context yes.
| cmacleod4 wrote:
| In Tcl you _can_ redefine "if", or even delete it entirely
| if you're crazy enough :-)
| spankalee wrote:
| 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 wrote:
| 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 wrote:
| See the `either` function further down either
| some-condition { print "was true" } { print "was false" }
| analog31 wrote:
| Interestingly, Excel provides "if" as a function:
|
| =if(condition, value-if-true, value-if-false)
| middayc wrote:
| Yes, Io language is closest to that syntax probably:
|
| https://iolanguage.org/guide/guide.html#Introduction
| analog31 wrote:
| I wonder if it's more readable to non-programmers than
| control flow.
| danlitt wrote:
| That's because Excel is a pure functional language :-)
|
| https://www.youtube.com/watch?v=0yKf8TrLUOw
| lisbbb wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| interesting. Give us an example of it's usage ... or how you
| implemented it?
| morcus wrote:
| I looked at the code - it uses ternary expressions.
| ozy wrote:
| Useless unless the logical operators receive their rhs
| unevaluated. And that is generalized as a language feature.
| sparkie wrote:
| 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 wrote:
| For lambda calculus, the motto is "when everything is a
| function". The boolean true is the function lx.ly.x, while false
| is lx.ly.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 wrote:
| yourIf == myId
| tromp wrote:
| 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 wrote:
| 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 wrote:
| There are other functions MyBool a -> MyBool a, such as
| not = \b x y -> b y x.
| nextaccountic wrote:
| This is
| https://en.wikipedia.org/wiki/Church_encoding#Church_Boolean...
|
| But there are other encodings
| tromp wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| > 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 wrote:
| 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`?
| correct_horse wrote:
| 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 wrote:
| My bad :), Gilligan typed language would resolve this at
| compile time. Will fix.
| andrepd wrote:
| Bravo, Vince.
| matt123456789 wrote:
| gcc() { curl -d '{"title": "Does this code look right?",
| "body": "$(cat $1)"' https://$HN_BASE/api/ask }
|
| export gcc
| emoII wrote:
| 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 wrote:
| > 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 wrote:
| You're correct, that is lazy evaluation. The entire article
| talks about lazy evaluation without mentioning it, which was
| my point
| middayc wrote:
| 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.
| the_gipsy wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| 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 wrote:
| 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.
| foofoo12 wrote:
| 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.
| padjo wrote:
| The apply* ?thing is kinda hard to parse, would turn me off
| seeing lots of that in a codebase.
| middayc wrote:
| 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 wrote:
| 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.
| Cosi1125 wrote:
| 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!)
| skybrian wrote:
| 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.
___________________________________________________________________
(page generated 2025-10-18 23:02 UTC)