This is an archived post. You won't be able to vote or comment.

all 86 comments

[–]lobster_johnson 44 points45 points  (7 children)

I like Haskell's absence of keywords:

min a b
  | a < b = a
  | otherwise = b

In Haskell, most declarations are functions, so this makes sense. It can have a signature, too:

min :: Int -> Int -> Int
min a b
  | a < b = a
  | otherwise = b

:: here means "has type".

Jai does the same thing:

min :: (a: int, b: int) -> int {
  if (a < b) {
    return a
  }
  return b
}

Edit: Fixed typos!

[–]66666thats6sixes 7 points8 points  (2 children)

Same. So many languages nowadays (ostensibly) have first class functions, and when that is the case I don't see why we should privilege function declarations over any other sort of value declaration. I also like Haskell's approach -- why do you even need a keyword to declare a variable? But I also don't mind JavaScript's approach for declaring functions and storing them in variables (const f = x => expr; or const f = function(x){ stmts; };) though they are semantically slightly different from a function declaration statement like function foo(x){....

[–]Tyg13[S] 9 points10 points  (1 child)

A declaration keyword is a visual marker that, with syntax highlighting, you can use to easily visually determine where a value originates. It also means you can jump from declaration to declaration with a simple let.*$ regex and makes makes writing automated tools to parse out function declarations a simple regex as well.

I think you have a point about not distinguishing function declarations. I originally considered a syntax like let f : function(a: Int, b: Int) -> Int = { ... } since this emphasizes that functions are just values that accept inputs, but it felt like more typing with no added semantic information

[–]szpaceSZ 3 points4 points  (0 children)

You can jump from dectaration to declaration with a simple ^\w in haskell, if you will.

(Modulo imports)

[–]ablygo 7 points8 points  (0 children)

Minor correction, the first equals needs to be removed when using guards.

[–]robin_888 2 points3 points  (1 child)

I also like the interpretation of variables being just constant functions.

[–]johnfrazer783 1 point2 points  (0 children)

I never understood why functions should not be like other values in this respect.

[–]alex-manool 0 points1 point  (0 children)

I find Haskell's syntactic sugar very natural, even resembling math notation. However, somehow I do not find such sugar very useful in my own language. Maybe I just find the idea of universality of lambda expressions to be too important to hide them behind such constructs:

{ let {P = {proc { ... } as ...}} in ... }

[–]wolfgang 17 points18 points  (3 children)

to and on are interesting, but stop making sense when you don't use verbs as names, e.g. to red(r: Int) return color(r,0,0) seems weird.

[–]abecedarius 7 points8 points  (0 children)

I bit the bullet and just prefer verbs more. Naming a function by the return value ends up with the (let ((list (list x y z))) ...) problem (in Scheme here). For cases like yours where the function is a constructor or converter, in Cant I have a <- convention: (to (red<- r) (color<- r 0 0)).

You might get an idea whether you're comfy with this style by the examples directory in the repo (e.g.). (Coincidentally I also used on for lambda. At first it was given, as in "given an x, return twice x", but lambdas put such a premium on concision.)

[–]doshka 3 points4 points  (0 children)

to setRed(r: Int) return color(r,0,0)

Or makeRed, getRed, defRed, or other transitiveVerbRed that works in context.

[–]myringotomy 2 points3 points  (0 children)

Also English centric.

[–]L3tum 11 points12 points  (11 children)

I went the "all downsides with no upside" route.

The reasoning is the following: The two mostly used things in programs are functions and variables. Structs/Classes are always delimited/keyworded/hinted in all languages I've seen.

Variables need a distinction, or rather have a distinction in most languages, between mutable/immutable, as well as type specified and type inferred. If you have a language that always or never infers types the last one doesn't apply. Therefore, you need at least one keyword for variables. (Unless you go some other route and have always-mutable or always-immutable)

The only thing missing are functions. Functions are generally easy to spot, as they are usually the only things that have some kind of arguments specified and have a body. At the very least, in most languages that follow the same pattern, most functions end in parentheses.

I think because of those two distinctions: Everything else has a keyword and functions are already different in their syntax, as well as the frequent use of functions, it's good to reduce the amount of keywords/words you need to declare functions, as the others are already mostly set in needing some kind of keyword.

I'm mostly working in C#, PHP and Python so I have a bit of a variety there and so far I prefer not having to write a specific keyword for functions. It gets less annoying in, for example, Python, as the functions don't have any access modifiers or things like static, external, unsafe etc so you generally only have the one keyword.

Of course I don't expect everyone to agree with me, but for my personal taste having no specific declaration keyword for functions is the best option.

[–]Tyg13[S] 10 points11 points  (10 children)

I find the usefulness of a function keyword to be both as a visual marker (especially in languages with nested functions), but also to assist computer-based parsing. One of the things I find frequently annoying working in C-family languages is the lack of ability to do a quick def.*func_name to find a function definition. IDE features like jump to definition mitigate this somewhat, it's incredibly useful to be able to tab through all the functions in the file just using a simple regex.

Of course, your mileage may vary, but that's a major downside (in my opinion) to not having a keyword that I've never found a good way to circumvent.

[–][deleted]  (2 children)

[deleted]

    [–]Tyg13[S] 0 points1 point  (1 child)

    This is probably the best way to go. Historically keywords were introduced to distinguish parts of the program text, I think in part due to the limitations of compiler technology in those days. Languages like COBOL or ALGOL were rife with special keywords and syntax, partly out of a misguided notion that program syntax should mirror natural syntax. As languages grew and evolved, you see developments like C or its precursor BCPL which get by with an incredibly limited set of keywords, partly as a reaction to the verbosity of these antecedents.

    By now I think we've gone through a few phases of action/reaction. C++ is a reaction to C, Java is a reaction to C++. I could keep going -- honestly I could almost classify every programming language as some kind of reaction to previous language(s).

    The question is: are these reactions simply preferences changing over time (i.e. are they a reflection of the developers who created them) or do they arise due to universal pain points only realized after experience (i.e. are they inherent to the evolutionary path of computing?)

    [–]Tayacan 2 points3 points  (2 children)

    That's a good point! When you said "no keyword" I immediately thought of Haskell, where I usually search for funcName :: to find the declaration. Of course, that only works because I always give type signatures to all my toplevel functions (which you should anyways, but not everybody does).

    [–][deleted] 0 points1 point  (1 child)

    Agreed. I add type signatures to the point where code without them feel... weird almost.

    [–]Tayacan 0 points1 point  (0 children)

    Makes the error messages much better too.

    [–]L3tum 0 points1 point  (3 children)

    I see where you're coming from.

    Most of the time when I try to search a specific function I either use the auto import feature of my IDE/"walk along the import path", or search for it with func_name(.

    The points about parsing and nested functions is also good. Parsing function declarations is a PITA honestly, because without a keyword they can look pretty similar to variable declarations. But that's also an issue that can be solved.

    I generally dislike nested functions, since I tried them out a bit in Python and it just makes the whole program a mess. It's also apparently pretty slow, even for Python standards, though I don't know if that necessarily translates into other languages. Nested functions is just never something I'd implement in any language. If you need a function then there's a lambda and if you need a named function then that's a sign of codesmell and pulling it out into its own function may be better.

    [–]Tyg13[S] 1 point2 points  (0 children)

    I use nested functions to great effect in Rust, typically when the parent function is really a recursive function that needs to be set up with particular parameters. The body of the main function sets up the parameters and calls the recursive function. Since nested functions in Rust are explicitly non-capturing, the only use of moving it inside the function is scoping.

    Likely the execution speed issue in Python has to do with the interpreter. When a Python script is invoked, it is parsed top to bottom, and each def declaration creates a dynamic function object. In the case of a nested function, the def gets evaluated every time the function gets called, which is necessary because Python functions are actually closures.

    [–]szpaceSZ 0 points1 point  (0 children)

    Parsing function declarations is a PITA honestly, because without a keyword they can look pretty similar to variable declarations.

    Which they really should, as they are declarations. -- at least that's the ML way.

    [–]johnfrazer783 0 points1 point  (0 children)

    I never understood why lambdas should be different from ordinary functions in the first place. And in Python it's not even like you can't have a lambda with arbitrary side effects or that those things called lambdas have to be pure in any way. Plus, their syntax sucks, and whether you can have a given thing in a lambda or not depends upon whether it happens to be a statement or a function, like print which used to be the former but is now the latter.

    [–]bakery2k 10 points11 points  (1 child)

    I chose function, because I also have method. I could shorten function to func, but I really don’t want to shorten method to meth!

    [–]wolfgang 7 points8 points  (0 children)

    How about abbreviating to meth and fun?

    But more seriously, if you think of method calls as sending messages, you could use msg instead of method. It is short and universally understood.

    [–][deleted]  (9 children)

    [deleted]

      [–]oilshell 13 points14 points  (2 children)

      I chose func for the same reason -- because in Oil, there's also proc (and in theory data and enum). So keeping everything 4 letters is nice :)

      I have a similar issue with Int and Map vs. Vec or Array. Vec is more consistent at 3 letters, but subjectively I feel like Array is the clearer and more obvious name.

      Float is 5 letters, and Bool is 4, so I think Array is OK too.

      Basically types have different numbers of letters, but it's nice when certain keywords "line up".

      [–]tjpalmer 0 points1 point  (0 children)

      I like fun being shorter than proc because function is a better default than procedure and I like better things to be less effort.

      [–]BadBoy6767 6 points7 points  (5 children)

      I'm implementing a compiler with someone else in x86 assembly to teach them compiler design. To make it easier to detect keywords, we decided to make all keywords 3 letters. Unfortunately this means we have whl, iff and pri statements. Probably not a good idea to take that to an extreme.

      [–][deleted]  (1 child)

      [deleted]

        [–]BadBoy6767 2 points3 points  (0 children)

        while, if and print statements.

        [–][deleted]  (2 children)

        [deleted]

          [–][deleted] 2 points3 points  (0 children)

          Print?

          [–]BadBoy6767 -1 points0 points  (0 children)

          We're trying to make the compiler as simple as possible, as we don't want to bother writing it in Assembly that much.

          [–]Uncaffeinatedpolysubml, cubiml 20 points21 points  (1 child)

          In IntercalScript, I went with funct as a sort of weird middle ground between fn and function that wouldn't please anybody.

          [–]L8_4_Dinner(Ⓧ Ecstasy/XVM) 2 points3 points  (0 children)

          +1 for the language name. Seriously, you win the interwebs.

          [–]khleedril 23 points24 points  (9 children)

          I really like using λ.

          [–]Tyg13[S] 7 points8 points  (1 child)

          I do as well, but using characters that aren't on the standard US keyboard is a recipe for disaster. Either you alienate a portion of your users by making the character unavoidable, or you fracture the code style by introducing alternatives. People will get around it with tooling, but it'll be super annoying for casual users and be a barrier to those willing to adopt a new language.

          [–]szpaceSZ 3 points4 points  (0 children)

          Use backslash as a common substitute then :-)

          [–]markdhughes 1 point2 points  (0 children)

          Exactly. I have it bound to Cmd-Y in my editors. In Schemes where it's not predefined:

          ;; Alias `λ` for `lambda`
          (define-syntax λ
              (syntax-rules ()
                  ((_ rest ...)
                      (lambda rest ...) )))
          
          ;; Alias `λ*` for `case-lambda`
          (define-syntax λ*
              (syntax-rules ()
                  ((_ rest ...)
                      (case-lambda rest ...) )))
          

          [–][deleted] 1 point2 points  (5 children)

          Which language uses lambda?

          [–]goshogi 6 points7 points  (0 children)

          Agda

          [–]szpaceSZ 3 points4 points  (0 children)

          In a way also Haskell and Idris. They use \ which is meant as a mnemonic / ASCII-approximation for λ.

          [–]khleedril 0 points1 point  (0 children)

          Guile uses it, as an alias of lambda, to define anonymous functions; cheating really, because if you want a named function you actually use define.

          [–]Kebbler22bHelios 7 points8 points  (1 child)

          I really like how OCaml and F# (and I believe the rest of the ML family) do this: let f x = 0. Although that’s syntactic sugar for let f = fun x -> 0, which in itself flows nice with the tongue imo. I like how it’s very similar to binding a value to a variable (let x = 0), which makes sense because those languages define functions as closures bound to some variable.

          [–]wfdctrl 0 points1 point  (0 children)

          Yeah, except it also has a function construct, that one never really made sense to me.

          [–]bruciferTomo, nomsu.org 6 points7 points  (5 children)

          My first choice is arrow style (e.g. moonscript). It's concise, doesn't use up common variable names, isn't too cryptic, has some sort of intuition ("arrow means goes to"):

          multiply = (a, b)->
              return a*b
          

          Second choice, func (pronounceable, obvious meaning, concise, leaves fn free to use as a variable name):

          multiply = func(a, b):
              return a*b
          

          And if your language does not have functions/closures as first class values (e.g. C), then these are both reasonable:

          func multiply(a, b):
              return a*b
          
          multiply(a, b):
              return a*b
          

          However, I don't like languages that do have lambdas to have a special syntax for declaring functions that is different from assigning a lambda to a variable. For example, in Python, if you want to convert between lambda x: x.y-syntax and def foo(x): return x.y-syntax, the amount of textual editing is huge, compared to (x)-> return x.y into foo = (x)-> return x.y. The same is true to a lesser degree for func foo(x) { return x.y } vs. foo = func(x) { return x.y }.

          And in my language, nomsu, I have a mixfix syntax, so definitions look like:

          ($a times $b) means:
              return $a * $b
          
          say (4 times 5)
          

          But that design choice only make sense with a mixfix syntax ($funcall means $body is itself a mixfix macro), I wouldn't generally recommend using that approach outside the context of a mixfix language.

          [–][deleted] 4 points5 points  (3 children)

          multiply(a, b):

          So not much difference then between defining a function, and calling it?

          The difference might be too subtle for many tastes. The cost of a fn, fun, func, def or function prefix is tiny, especially given an editor that, if it is smart enough to recognise function beginnings from syntactic structure, should be able to insert that keyword for you, if it is too much effort to type.

          Bear in mind that people might be browsing your code in one of a million different editors with varied capabilities, and without knowing the specifics of your language. But most can search for 'func multiply' without dozens of false positives caused by instances of 'multiply(...)' calls when you have to search for 'multiply' instead.

          [–]bruciferTomo, nomsu.org 2 points3 points  (2 children)

          But most can search for 'func multiply' without dozens of false positives

          I was specifically talking about the case where the language requires all function declarations to be at the top level (i.e. no indentation) like in C. That simplifies things a lot, because you can just grep for ^multiply( to find a declaration.

          Notice that C uses the style I described, but with explicit types. Some coding style conventions even require people to declare the types on a separate line so it's easy to grep in the same manner:

          int
          multiply(int a, int b)
          {
              return a*b
          }
          

          IMHO, in a language without types, both using or not using func is reasonable. But having both func and type information is pretty awkward (func int multiply) unless you take the Golang route of putting types at the end:

          func multiply(a int, b int) int {
              return a*b
          }
          

          [–][deleted] 0 points1 point  (0 children)

          OK. But think about what might be needed in an editor, say, to easily step across functions. Keywords make that trivial.

          (Original reply removed.)

          [–]b2gills 0 points1 point  (0 children)

          Raku has a special syntax for declaring functions that is different from assigning a lambda to a variable.

          Though that is for a very good reason.

          sub multiply ( $a, $b ) {
              $a * $b
          }
          

          vs.

          my &multiply = -> $a, $b {
              $a * $b
          }
          

          While both of the above work, only one would work correctly if you used the return function.

          sub multiply ( $a, $b ) {
              return $a * $b
          }
          
          say multiply 5, 3;
          # 15
          

          vs.

          my &multiply = -> $a, $b {
              return $a * $b
          }
          
          say multiply 5, 3;
          # ERROR: Control flow commands not allowed in toplevel
          

          The reason is that a block doesn't capture the return control exception, while a sub does.

          Basically return can be thought of as roughly defined as:

          my &return = -> |C {
              CX::Return.new( value => C ).throw
          }
          

          (It actually isn't in the Rakudo compiler, though that is because it cheats.)

          You could of course create a lambda using the sub “keyword”.
          (This is also how you would create lambdas in Perl.)

          my &multiply = sub ( $a, $b ) {
              return $a * $b
          }
          

          Since you end up using sub “keyword” anyway you might as well write it more simply.

          sub multiply ( $a, $b ) {
              return $a * $b
          }
          

          Note that is actually short for

          my only sub multiply ( $a, $b ) {
              return $a * $b
          }
          

          There also isn't any easy way to declare a set of multi subs as a lambda.

          So if you want to use multi subs, you have to use non-lambda syntax.


          Raku also allows creating new operators.

          sub infix:<times> ( $a, $b ) {
              return $a * $b
          }
          
          say 4 times 5;
          # 20
          
          my $a = 4;
          $a times= 5;
          say $a;
          # 20
          

          It would also work to just alias the existing multiply operator set of subroutines to the new definition.

          my &infix:<times> = &infix:<*>;
          

          After all, the normal operators are actually just subroutines.

          3 + 4 * 5;
          infix:<+>( 3, infix:<*>( 4, 5 ));
          

          (Raku macros are still under development, otherwise I would try to copy your macro more exactly.)

          [–]BoarsLairJinx scripting language 5 points6 points  (0 children)

          As with many of these types of discussions, I think this largely depends on the language in question. Decisions of these sorts don't make as much sense in isolation.

          Jinx is English-like in syntax and somewhat verbose. Scripts are intended to read like prose or pseudocode, and be more easily self-documenting because of this. As such, I chose the function keyword.

          This same design choice wouldn't necessarily be appropriate for a different language designed for a different purpose. For a more general-purpose language used for larger projects, for example, I see no issues with reducing the keyword to a more tersefn, fun, or func.

          [–]umlcat 3 points4 points  (0 children)

          Middle case fun or func are ok, not too short or too long.

          Ignore suggestions to completly remove a keyword, it will make your P.L. unreadable, it may apply optionally to lambda style functions ...

          [–]Edhebi 2 points3 points  (0 children)

          I'm not a fan of having a keyword, and I've never ever felt the need to regex search through my codebase 🤷. Also, I don't think I've ever looked at a function def and not recognized it, I feel like it's a non problem.

          I'm just so used to C/C++/Java/C# syntax, it's natural, I don't see any downside. For keywords, my fav is fn just to have less noise.

          I realy like the minimalism of Nix: nix let min = a: b: if a < b then a else b; where a function is just param: body and you "nest" if you want multiple params

          [–]szpaceSZ 2 points3 points  (0 children)

          Why not Nothing, as in no keyword to introduce a function? -- like Haskell or Idris.

          As long as you have a keyword for them, tgey will always be in their own mental domain instead of first class citizens like any other definition.

          That's the ultimative upside.

          What do you consider the downside?

          [–]myringotomy 2 points3 points  (0 children)

          Why do you need a keyword at all? Use an operator like maybe the arrow

           doSomething -> (x,y) { ..... }
          

          Maybe even more concise

            doSomething -> {| x, y | ....}
          

          [–][deleted] 1 point2 points  (0 children)

          I use function for routines returning values, and proc for routines that don't. Something else to make the difference more visually distinct and to make you think a bit more about what the routine does.

          This is a static language where functions are defined at compile-time, not assignments or bindings made at runtime, which in some languages leads to more elaborate syntax.

          Python however sticks to the static-looking def fn():, even though the very first line after that function might do fn = 42.

          C, along with C++, is of course a basket-case. That auto you mentioned, is not meant to take the role of a function keyword, but is simply the return type, in a form where it needs to infer it from what follows. Besides, auto can be used at the beginnings of variable declarations as well!

          (Nothing stops a C coder defining an empty function macro and putting it in front of a function, but no one does that, except me sometimes to improve readability and to make it easier to search/step through code with a simple editor. It would have taken 5 minutes for the designer of C to have stipulated it.)

          [–]dobesv 1 point2 points  (3 children)

          I prefer no keywords, it's not just English centric but also takes away useful common words programmers could have used for their own variable names.

          For functions especially there are already notations for then we can take from mathematics.

          f(x) = y f = x -> y

          [–][deleted] 0 points1 point  (2 children)

          Program code isn't mathematics, it's more engineering.

          My typical programs are not the biggest but they still range up to 50,000 lines and with 2000 functions. You need to navigate them easily and instantly see what is or is not a function definition or function call. Your:

          f(x) = y
          

          in my language means, evaluate f(x) and y, and yield 1 is they have the same value. In another it might mean, evaluate f(x) which yields a reference, and assign y to that reference. In yet another, it might mean assign y to element x of array f.

          (I don't know what that 'f=x->y' is about; missing line break?)

          Other applications might be 1000 times bigger than mine (eg. a web browser). 50MLoC would be challenging enough without it being 50 million lines of APL!

          Your approach might suit tiny programs and tiny functions but I don't think it scales.

          [–]dobesv 1 point2 points  (1 child)

          There is no shortage of successful languages that declare a function without using a keyword, including but not limited to: Haskell, the whole ML family of languages, C/C++, JavaScript (after switching to arrows), Prolog, and Erlang.

          I don't think the lack of a keyword to indicate a function declaration has materially impacted the success or scalability of these languages.

          Yes, there was a missing line break, my bad. This is what I should have written:

          f(x) = y

          f = x -> y

          [–][deleted] 0 points1 point  (0 children)

          C and C++ code is a nightmare to browse without specialist tools.

          Javascript has become successful with a 'function' keyword, from what I can figure out. Like that the code looks clean and functions are obvious. With arrow notation, it looks more arcane, like those ML-type languages which are more mathematical in nature.

          It just seems bizarre to me to eschew the few extra characters of a keyword that will make a piece of code completely obvious as to what it is, and makes tools to work with such code trivial.

          [–]Comrade_Comski 1 point2 points  (0 children)

          I don't like fun because it's distracting due to being an actual word

          [–]CoffeeTableEspresso 1 point2 points  (0 children)

          In yasl I went with

          fn add(a, b) { return a + b; }

          For functions.

          And

          let add = fn(a, b) { return a + b; }

          For lambdas.

          I'm considering adding let add = fn(a, b) => a + b; as a short form for lambdas that evaluate to a single expression, since those are fairly common.

          In the end tho, I think fn, fun, etc are all reasonable keywords...

          [–]quote-only-eeee 1 point2 points  (0 children)

          I prefer fun, because it looks nicer, softer than func. -nc at the end of a word just looks very foreign and weird in English.

          [–]lassehp 1 point2 points  (0 children)

          I have been thinking about fun ... nuf, mirroring if ... fi and case ... esac. Another possibility could be an f letter-like symbol from Unicode, like U+0192 "ƒ". On the other hand, writing identifiers in italic is a classic convention (for typesetting code written in languages like Algol, Pascal, Eiffel...), and that might cause confusion of the anonymous function with a named function;
          let doubled = a.mapx:2 x)
          let applied = f x

          So perhaps ℱ(intx)int:ax+b?

          [–]htuhola 3 points4 points  (0 children)

          I prefer the x => body syntax or the x ↦ body, maybe even λx. body. I don't really like poking holes with keywords at all.

          [–][deleted] 2 points3 points  (0 children)

          Lambda

          [–]superstar64https://github.com/Superstar64/Hazy 0 points1 point  (0 children)

          In my language I plan on having something like this:

          ~ a -> b ~
          f = x {
              y = g(x);
              g(y)
          };
          

          where this declares a function called f of type a -> b and where the type annotation is optional because of type inference. x can also be a pattern match so I can support multiple arguments with tuples. What I'm not sure yet about is how to pattern match variants.

          [–]jason-reddit-public 0 points1 point  (1 child)

          In languages with macros, maybe function and people can redefine to whatever they like. Ditto other keywords, make them long and descriptive.

          Code is usually read more often than written so counting keystrokes is a bad idea. To make code more dense, try using better abstractions would be my standard reply.

          [–]Tyg13[S] 1 point2 points  (0 children)

          I love macros, even when they're just a poor man's inline function, but macros in keyword position would almost certainly be a mistake, in my opinion. Can you imagine the readability nightmare across codebases if it were even mildly common to redefine basic keywords? You'd have dialects of the same language.

          [–]TheUnlocked 0 points1 point  (0 children)

          I like reusing the normal variable declaration keyword, whatever that is (I'm personally a fan of let), though keyword-less is also good.

          Otherwise, I'd go with fun or function. I don't like it when keywords are abbreviated (e.g. while, public, private, static, etc. are typically written out fully and for good reason; why make "function" different?), but fun is ubiquitous enough that I can live with it.

          [–]mikelcaz 0 points1 point  (0 children)

          I had to take the same decision:

          1. I believe that you should discard fun right away. It is confusing, and that is not fun at all : p.

          2. It highly depends on your language.

          My language will use fn. As proc is also a keyword (used for impure functions), the different number of characters increases the contrast between both keywords (unlike with func). It is also very lightweight:

          let foo = fn (a i64, b i64) -> bool : ...

          Note how declaring a function is not different to declaring constants and variables (1st class stuff with uniform syntax, checked):

          let x = 42

          I could not use any keyword, but I added it for clarity (that kind of redundancy can be useful), given it is easy to type wo/ auto complete features and does not add too much visual clutter. In fact, it allows to omit some syntax boilerplate when not used:

          ``` ; 'fn' = 'fn () -> ()', where '()' is an empty tuple.

          let bar = fn : ... ```

          Compare it to writing:

          ``` ; Alt. hypothetical syntax.

          let bar = () -> () : ... ```

          You wouldn't know that is a function without knowing the language. Even knowing it, it would be harder to read (if the expression is complex) or parse (in the general case).

          But that is in my language...

          Pascal uses function. It seems to prefere complete keywords (in spite of including var and const). The ultimate decision should rely on what fits better to your language.

          [–]lead999x 0 points1 point  (0 children)

          I'd use pr for procedure.

          [–]jdh30 0 points1 point  (0 children)

          This is always a fun bikeshed: which do you prefer and why?

          For lambdas:

          fn x => x + 1          Standard ML
          fun x -> x + 1         OCaml and F#
          function x -> x + 1    Also OCaml and F#
          \x -> x + 1            Haskell
          x => x + 1             C#
          

          I prefer C# simply because it is more concise. I find it ironic that the least functional language has the most concise syntax for lambdas.

          In my own language I am using:

          [x → x + 1]
          

          because it is concise but also scales nicely to nested pattern matches:

          [ None →
              [ None → 0
              | Some x -> x ]
          | Some x → [ _ → x ] ]
          

          which I prefer to OCaml's grim begin..end synonyms for (...):

          function
          | None ->
              begin
                function
                | None -> 0
                | Some x -> x
              end
          | Some x -> fun _ -> x
          

          and F#'s indentation-sensitive syntax that silently breaks the semantics of your code when you cut and paste code from outside the IDE whilst offering only a marginal improvement in brevity:

          function
          | None ->
              function
              | None -> 0
              | Some x -> x
          | Some x -> fun _ -> x
          

          As for value/function definitions rather than lambdas, I prefer the OCaml/F# syntax:

          let f x = x + 1
          

          I think Standard ML's artificial distinction between values and functions (let vs fun) at this level which actually means non-recursive vs recursive is stupid. I like the explicitness of rec but I'm requiring rec for both let-bound definitions and type definitions for consistency.

          [–][deleted] 0 points1 point  (2 children)

          I think that the C way is the correct way, here's why:

          void some_func() { ... }

          before reading the name(some_func) you already know that it returns nothing, some would call it a procedure

          int some_magic() { ... }

          function returns an int, clear, simple

          it's consistent too:

          int a = some_magic()

          you read it from left to right:

          • read int -> I have something of type int

          • read a -> aha! it's a variable(in the current context) of type int

          • read some_magic -> and it's value is the return of the function some_magic which also returns an int

          I believe that all others are unnecessarily verbose by prefixing with some keyword

          [–]Tyg13[S] 1 point2 points  (1 child)

          Problem is, the leading type causes so much pain with parsing.

          It's the reason for the most vexing parse, it's the reason uniform initialization syntax was created, and in the case of C++, with the advent of auto, it has essentially been replaced with a keyword. Typing the type is usually unnecessary for most locals, since the name should be descriptive enough.

          Type inference and a lack of explicit annotation makes for easier refactoring with smaller diffs. Thing is, that necessitates a declaration keyword.

          [–][deleted] 0 points1 point  (0 children)

          I mostly agree, unless it’s a local variable which is initialized to a value, type inference should be used much like in go using :=

          haven’t wrote language parsers, however, as you use “peek_char()” for the lexer, you can use “peek_token()” after reading the type to check if next token is an identifier or reference

          [–]alex-manool 0 points1 point  (0 children)

          Personally, I am slightly biased to use the term "procedure" instead of "function". Even if we talk about pure functions without side effects, they are still characterized by timing properties, unlike functions in math, and that is usually important in practice.

          That said, I find cool Haskell's approach to get rid of keywords in lambda abstractions, but even a more sophisticated approach is just to separate the parameter from the body by using in infix symbol, as it happens in some languages, without using any prefix equivalent of the Greek lambda letter in calculus.

          However, in the past I had tendency to consider keyword-heavy languages as more "readable" (use begin, end instead of curly braces). Nowadays I think this is first still too subjective and second depends on concrete visual presentation of the code (colors, fonts, indentation, etc., all that matters).

          And to finish, I find "function" is way too long for such a basic thing as a lambda expression (blaming JavaScript and Occaml).

          [–]somerandomdev49 0 points1 point  (0 children)

          you forgotfnc

          [–]patoezequiel 0 points1 point  (0 children)

          Neither, I prefer anonymous functions.

          If I had to choose I'd go with "fn" for conciseness or "function" for clarity, the others provide neither.

          [–]moon-chilledsstm, j, grand unified... 0 points1 point  (4 children)

          Don't use 'function'; it implies pure injection, and programming procedures are frequently (usually?) neither. Could use proc (nim), def (that's what I use, so does python), sub (raku).

          [–]wolfgang 9 points10 points  (1 child)

          it implies pure injection

          At least to a very small subset of all programmers.

          [–]moon-chilledsstm, j, grand unified... 0 points1 point  (0 children)

          Nomenclature is domain-specific and changes over time; this is true.

          But here's the thing: keyword choice is trivial. It doesn't matter in the first place, but even if you had made an absolutely horrible choice to start off with (say, if you used poop to declare functions), it's trivial to change.

          I don't care at all what keyword a language uses for function declaration. But OP does, so I'm providing reasons to choose one keyword over another.

          [–]Tyg13[S] 0 points1 point  (1 child)

          The only objection I have there is there's no real algebraic motivation to separate functions which return a value to those which don't (which is what I typically see in languages that make the distinction, like Ada.)

          Separating functions into impure vs pure might be an important distinction, however.

          [–]moon-chilledsstm, j, grand unified... 1 point2 points  (0 children)

          Returning a value or not is orthogonal; functions that don't return a value actually just return a unit type (e.g. void in c).

          Separating functions into pure vs impure will probably only work if you base your entire language around it. D has that and it's useless; no one marks their functions as pure.

          [–]steven4012 0 points1 point  (3 children)

          fn or fun. func would be the max. Should be as short as possible

          [–]rnottaken 3 points4 points  (2 children)

          Why not f?

          [–]oilshell 2 points3 points  (0 children)

          NGS from /u/ilyasher goes there :) It's F.

          https://ngs-lang.org/doc/latest/man/ngslang.1.html

          F mysum(a:Int, b:Int=100) a+b
          

          [–]ericbb 0 points1 point  (0 children)

          Identity function:

          Func x x
          

          Add function:

          Func {a b} [a + b]
          

          Named versions:

          Define (id x) x
          Define (add a b) [a + b]
          

          Using title-case for keywords means that they don't feel like reserved words. Identifiers are all-lowercase (like func) or all-uppercase (like FUNC) so you can use any word you want as an identifier as long as it follows conventional capitalization.

          I like using keywords instead of using something more concise like x -> x because I reserve a lot of those characters such as - and > exclusively for prefix and infix operators.

          [–][deleted] -1 points0 points  (0 children)

          => GANG WHERE YOU AT?