sshine 25 minutes ago

> Functions Are Asymmetric

No, bijective functions are symmetric.

Injectivity is enough to invert a function.

> Even though they may take any number of arguments, they must each have one return value. [...] This is true of all functions.

Mathematical functions take one value and produce one value. This is true of all mathematical functions.

Programming functions can be modelled this way by treating multiple input arguments as a single product, and non-scalar outputs containing multiple values as a single product.

Programming functions don't even have to always return one value:

  - Haskell's Void type
  - Rust's ! type
  - TypeScript's never type
all have zero values, meaning the function can't meaningfully return.

Arguably, programming functions must take at least one value as input, otherwise how can they be called?

In that sense, programming functions are asymmetric: It rarely makes sense to write a function you can't call, but it often makes sense to write a function that never returns.

When does it make sense to write a function that you can't call? When the point of the function is to prove something as a result of being compiled. The value lies in the compilation, not in being called.

A better title for this article: Some Functions Are Asymmetric.

Which is less of a profound insight.

d_tr 4 days ago

Functions map members of a set A to members of a set B. These can simply be Cartesian products whose members are tuples. In my dream PL syntax a function call would be a function name followed by a tuple, and that tuple would be no different than the tuples you would use in any other part of the program (and so you could use all the tuple manipulation library goodies). If the function preserves any other structure of the type, like an identity element, that could be stated so you could have morphisms. And that identity element or other properties could be declared just as other stuff like 'const' are declared, and since the compiler can't verify all these stated properties, it's on the user to provide correct info, just like it's on the user to write a correct program, so nothing lost here, and anything more, like verification by the compiler, would be a bonus.

Mathematicians have been packing all this stuff nicely for a couple of centuries now, maybe we could use more of their work on mainstream computing, and it could also be a nice opportunity to get more people to appreciate math and structure.

Something that has side effects all over the place should just not be called a function, but something else, maybe "procedure" would be an appropriate, clear term.

  • snthpy 9 minutes ago

    > In my dream PL syntax a function call would be a function name followed by a tuple, and that tuple would be no different than the tuples you would use in any other part of the program

    So PRQL (prql-lang.org) is kind of like that, with the limitation that control flow is limited to the List Monad bind, i.e. the tuples from one step are piped to the function call in the next step one at a time producing 0..* result tuples and the resulting multiset is flatmapped. At the moment it just transpiles to SQL but a couple of months ago I was exploring different Lambda Calculi and how to extend this to a more general PL. Alas, that won't take shape until AI is at the level that it can write that code for me. I guess LINQ and similar Language Integrated Query Languages already provide this functionality.

    P.S. Writing the above made me think that it's not quite what you asked for; in the PRQL case each function receives an implicit `this` argument which is the tuple I was thinking of. However the function can also take other arguments, including keyword arguments. Those are arbitrary. I guess they are implicitly ordered and could be represented as a tuple as well. What would you see as the benefit of that?

    > (and so you could use all the tuple manipulation library goodies)

    Other than indexing into tuples, I can't really think of anything else, at least for single tuples. I initially thought of something like `zip(*args)` but that's only really useful when you have list of tuples or tuple of lists and then you're back in PRQL land. Indexing into tuples is also brittle and does not produce self-documenting code so I prefer the PRQL and SQL namedtuples/structs where fields are referenceable by name.

    I have this suspicion that PRQL functions are parameterised natural transformations but my Category Theory at that level is too rusty to check without extra work. If that's the case though then having the explicit function arguments be simple values feels justified to me since they're just indexing families of related transformations and are not the primary data being transformed (if that makes sense?).

  • WJW 4 hours ago

    Haskell is much like this? A function like `borp :: a -> b` maps from type a to type b. If you want to have side effects like mutable state, you need to encode that in the function signature, like `borpWithState :: a -> State s b`, where s is the type of the mutable state.

    In this case it's almost the opposite of most programming languages. In (say) Ruby or Java, any function or method can do anything; write to stdout, throw exceptions, access the network, mutate global state, etc. In haskell, a function can only do calculations and return the result by default. All the other things are still possible, but you do have to encode it in the type of the function.

    EDIT: The annotations you mention with regards to identity elements etc do exist, but they live mostly on the data structures rather than on the functions that operate on those data structures.

    • defanor 2 hours ago

      Languages with dependent types (Agda, Idris, Coq, Lean) seem even closer to the description, with a user supplying proofs of properties. For instance, as in idris2-algebra [1]. One can similarly define isomorphisms, or other kinds of morphisms, by listing their properties, and requiring proofs to construct.

      [1] https://github.com/stefan-hoeck/idris2-algebra

  • agumonkey 5 hours ago

    There was one attempt at creating a language splitting both pure function and effectful procedures. Any construct with a procedure call was automatically/effectively typed as a procedure. But I can't recall the name so far..

    • paulddraper 4 hours ago

      That’s cool function coloring.

      C++ sort of has this, with const.

      • hardlianotion 4 hours ago

        I am a fan of C++ const, but I don't really see what it has to do with effect labelling in this way.

  • noelwelsh 3 hours ago

    ML (Standard ML, OCaml) functions idiomatically accept and return tuples.

    • thaumasiotes 36 minutes ago

      I'm pretty sure OCaml functions are monadic. They don't really accept tuples (unless you intentionally do something weird).

      Rather, if you define (in your mind) a function of three variables, the compiler makes that a function of one variable that returns another function. And that return-value function takes one variable and returns a third function. And that third function takes one variable and returns the result of the triadic function you intended to write.

      That's why the type of a notionally-but-not-really triadic function is a -> b -> c -> d and not (a, b, c) -> d. It's a function of one variable (of type a) whose return value is of type b -> c -> d.

  • thaumasiotes 42 minutes ago

    > Functions map members of a set A to members of a set B. These can simply be Cartesian products whose members are tuples.

    Well, a function can't be a Cartesian product unless set B has cardinality 1. It's perfectly coherent to view a function as a set of tuples, but it's not legal for that set to contain two tuples (a, b) and (a, c) where b ≠ c.

    > In my dream PL syntax a function call would be a function name followed by a tuple, and that tuple would be no different than the tuples you would use in any other part of the program (and so you could use all the tuple manipulation library goodies).

    This already exists. For example, that's how `apply` works in Common Lisp.

    https://www.lispworks.com/documentation/HyperSpec/Body/f_app...

        (apply #'+ '(1 2)) => 3
PropagandaDude 4 days ago

> Maybe that doesn’t seem strange. It’s just how we’re used to functions working. Ah, those steeped in functional programming might say, but maybe this is the wrong way to look at it. Because if we curry functions and use partial application, we can say that they always have one argument and return one value, and then they are symmetric.

Ah, those steeped in functional programming might say, but maybe this is the wrong way to look at it. Because if we represent the represent the functions with explicit continuations, we can say that they have N arguments and pass N arguments to their continuations, and then they are symmetric.

It seems like this has fertile overlap with Scheme and the (concurrent computatation) Actor model.

Of course, I can imagine the Execution control library authors know full well about those, with existing C++ goals and designs making that a bridge too far.

asimpletune 3 hours ago

Functions can accept/return multiple values though?

// In typescript const [a, b, c] = foo(d, e, f)

You could even pass this to itself

foo(…foo(d, e, f))

Also one definition of a function is a map from a domain to a range. There’s nothing that forbids multiple values, or is there?

  • maweki an hour ago

    The range can be a product type, as can the domain. Most languages are expressive enough that you can create the product type (struct). You're right on point.

gethly 2 hours ago

> One shall be the number, and the number shall be one.

Monty Python fan detected :D

ccppurcell 5 hours ago

Sorry if this is low effort, but I wanted to show some appreciation for a subtle Monty Python reference here! I won't spoil it though...