HackerTrans
TopNewTrendsCommentsPastAskShowJobs

crimsonalucard5

no profile record

comments

crimsonalucard5
·6 anni fa·discuss
I just go strict. Never use Any unless I absolutely must. Hunting down the types is just the price you pay for what is basically a huge reduction in hidden runtime errors.
crimsonalucard5
·6 anni fa·discuss
>you can map and analogize

This is a common thing for people to do during debates and it's not an effective form of proving anything. If you read my post carefully you will see that, again I did NOT do this.

What I did is defined what a function is, and defined How a unix program fits that definition.

This is not a "illustrate my point by 10 million examples while establishing nothing" It's a logical proof. Read it more carefully.

>However, what you've done at that point is rediscover that all these mechanisms are computational and thus at some level equivalent and transformable into each other, just like yoo can implement all of this with just NAND gates.

Transformational is not the right term. The term you're seeking is "isomorphic." First off you're not being clear about what you're saying so I'm going to make an assumption.

Here's what I'm assuming:

Your statement implies that because all things are "isomorphic" with NAND Gates and Computation in general therefore all things are "functions" including unix programs. If what you imply is True (which it is not by the way) then you're basically saying that I'm right but the statement I'm making is Pointless because everything can be a "function."

From another perspective what your statement implies is that any statement that anyone makes about whether or not a language is functional or not is pointless because it's all Turing Machines and NAND gates so everything is functional so who cares? This is obviously not the case. C++ is clearly not a functional language even though it can be coded to functionally behave as if it were and vice versa, but the language itself does not FIT the definition of a functional language, only a small subset of C++ may fit, but the language itself does not fit.

But let's get back to the part where you're wrong. NAND gates are functions, but not all Composition of Nand Gates Fit the definition of a function. Additionally the definition of a Turing Machine does not fit the definition of a function either.

Examine the JK FlipFlop: http://hyperphysics.phy-astr.gsu.edu/hbase/Electronic/jkflip...

You will see this basic computing component is constructed from nand gates but it's truth table is anything but deterministic. A JK flip flop does not represent a deterministic mapping between a codomain and a domain and is therefore NOT a function.

JK flip flops form the backbone of registers which are in turn a fundamental concept of computing machines. Computing machines themselves hold state via registers and thus computations are not deterministic as the computations rely on state stored in the registers.

Thus for computing as a whole, for programming languages as a whole, MOST of computing IS NOT Functional Programming because most computations rely on state and are not deterministic.

The greater majority of programs designed on top of a computation machine rely on stateful deterministic methods of computing: Java, C++, python almost every computing technology out there is NOT functional. Only a small subset of languages can fit the definition of a function or functional program. This INCLUDES "unix programs and Piping" in general, but most things DO NOT fit this paradigm.

You can configure these machines (and languages) to BEHAVE as if they were stateless just like you can configure functional machines to BEHAVE as if they were stateful via the "curry-howard correspondance" but this doesn't mean that "everything" is a function, the definition of a function itself actually refers to superficial differences outside of the isomorphism.

>You have not shown what you claim. ¯\_(ツ)_/¯

I have shown that your statements are not only an admission that I'm right, but that your statement is indeed wrong.

I didn't use any analogies here. I went straight into situations that disprove your statements derived directly from your wording. If I used an analogy you will find the Use of the word "like" somewhere which did NOT occur.

Also I'm not the one being pedantic here. You're the one who initially took the statement into computing theory. Literally unix piping is a highly functional paradigm. It is obvious from an intuitive and lay-mans perspective. My initial post stating this has roughly 40 upvotes so you will find that most people agree with me.
crimsonalucard5
·6 anni fa·discuss
No I did not do what you wrote.

Here's what I did: I defined what a function is, then I described how a unix program fits the definition of what a function is...

In short a unix program with stdin as domain and stdout as a codomain is a function but a function is not necessarily a unix program.

I think what's actually going on is you didn't read what I wrote very carefully. You sort of just skimmed over it. I can't blame you, it is rather long and detailed...

But if you want to have a meaningful discussion you need to read it and ask questions about things you don't understand.
crimsonalucard5
·6 anni fa·discuss
Unfortunately for you, it does add to the debate. I just proved to you a spectrum exists and some tools are better than others.

I find your attitude offensive btw. Please tone it down and respond with something substantial other than "You're wrong."

If you feel it doesn't add to the debate tell me why you think so and I'll tell you why you're wrong.

edit: stop flagging my posts for no reason. I'm literally just having a conversation with you, not insulting you or offending you. Don't use the HN tools to lash out emotionally. Please respond with reason and logic and I will do the same. Your last flag on my other response, btw was denied.
crimsonalucard5
·6 anni fa·discuss
I use to be pretty negative about JS, but typescript saves the day. The lego aspect of functional programming always becomes more evident when you have static types.
crimsonalucard5
·6 anni fa·discuss
I don't think dynamic typing or static typing really applies.

In either case you have to compose functions With the correct types otherwise you hit an error. The difference with dynamic types and static types is when this error occurs. Run time for dynamic types and compilation time for static types. Either way some type of error will always occur regardless.

Though I agree with you if you have a limited number of types things become easier to deal with.
crimsonalucard5
·6 anni fa·discuss
Yeah, I'm just saying it can work and it can work remarkably well and efficiently. It's just probably too complex for our limited intelligence to handle.
crimsonalucard5
·6 anni fa·discuss
>I firmly believe that, there’s no language or tool that suddenly makes you create better things.

This belief makes no logical sense. I can easily define a language for you or a tool that can prevent you from EVER making a good thing and I can easily define a tool that can ONLY allow you to make one good thing.

For example a programming language that always compiles into a program that does nothing Or A program that always compiles into a performant news website. Ludicrous examples, I know, but important nonetheless.

Why are they important? Because these tools illustrate the existence of extremes. If extremes exist then the entire domain must be a spectrum of some sort.

Logically, Within this spectrum there are tools that can very much almost magically help you make great things and tools that do the exact opposite.

By logic All the tools we have must currently live somewhere on this spectrum.

It's just that the problem is so broad we can't prove exactly where on the spectrum each of these tools actually lives... hence debate.

However the existence of extremes proves that a spectrum exists and all tools must exist somewhere on the spectrum and some tools must be better than others.
crimsonalucard5
·6 anni fa·discuss
>Functions run to completion and return their result. Filters tend to run concurrently and, importantly, do not return their results, they pass them on to the next filter in the pipeline.

Let me address this point so it is more clear. Functions by definition do not need to "run" at all. The underlying implementation of a programming language may "run" a function lazily, eagerly or concurrently but that is not what the Definition of a function is describing.

A function is simply a relation between two sets, one set called a codomain, another set called a domain is deterministic. That is all.

So within unix piping the domain is stdin, the codomain is stdout, the function is the unix program. This paradigm fits the definition of a function and as long as that "function" remains deterministic and compose-able it fits within the paradigm of functional programming BY definition. there is no need to discuss "similarities" here, because it fits the definition therefore it is functional programming. Of course we can get pedantic about certain non-functional use cases but in basically you are fully aware of the generality I'm describing. No point in getting pedantic.

Also keep in mind when you "execute" these functions it does not matter HOW these functions are executing whether concurrently, lazily or eagerly. The only important thing is the relation between the domain and codomain. That is all.

>The single type is also a restriction relative to the functional model, and crucial to (syntactic) composability.

Every unix program must internally deserialize this string type and serialize it again for output. The typing must be handled regardless. It seems like you don't need to deal with it but it's dealt with regardless. You also get inevitable deserialization problems of incorrect types. You do not escape typing in the unix world.

>But they are not actually equal (never mind the same), at least not in typed FP, and most FP is typed.

A relation that takes a string from a domain and returns a string that is from a codomain is a function that is part of functional programming. This is EXACTLY what a unix program does when relating stdin to stdout and therefore it IS a function and it is FP By definition.

You can look at it from another direction. A function that returns an Array does not fit the definition of what a unix program should output to stdout therefore a function IS NOT a unix program. BUT a unix program IS a function.

Formally unix programs are just the set of all functions where the domain and codomains are strings.

>>Both f' and f are equivalent in theory. >But they are not actually equal (never mind the same), at least not in typed FP, and most FP is typed.

The mathematical term is "isomorphic" meaning you get the EXACT same properties going one way vs. the other way. It's the syntactic sugar that prevents composition. Using multi-arity functions and single arity functions with tuples only involves two additional parenthesis:

   f(a, b)
   f'((a, b))
It's just syntactic sugar that causes a difference to occur.

Arity and tuple types are well described to be isomorphic in category theory. I'm not making this up.

One way to think about an isomorphism is that if two things are isomorphic then they are just different perspectives of the exact same thing. Any differences are superficial or illusory.

>Exactly. It is hidden in Unix and not in FP. That's a difference.

Exactly what? If you're not dealing with the type conversions someone else has too. Just because it's hidden from you doesn't mean it doesn't need to be dealt with. Someone has to serialize things and deserialize it. Even from the command line level you cannot escape types. Let's say you have a unix program that can only take numerical strings from stdin. Let's say you pipe english letters to it... if you do then you will have still triggered a sort of type error despite everything being typed as strings in unix. It's just not handled explicitly as a system error.

You cannot escape typing even in the unix world because it is in the end just FP.

>So: it is good to recognize that 'X is similar to Y', but that doesn't imply that 'X is just Y'.

Except we're not talking about similarity. Like I said above X is just Y BY definition.
crimsonalucard5
·6 anni fa·discuss
>Filters tend to run concurrently and, importantly, do not return their results, they pass them on to the next filter in the pipeline.

Filters are functions. They are one and the same. For unix it's a function that takes in a string and outputs a string. The unix world reduces everything into a singular type. You will note that unix "filters" cannot compose with functions of "other" types either. If unix included types like arrays or ints in stdout you would have the same typing problems as you have with functions.

In the unix world all your types are strings so things seem simpler, but in reality your little unix programs need to deserialize the strings into proper types in order to do anything meaningful. You could achieve the same thing if you used functions that returned strings all the time then did internal deserialization but that's not really a solution.

I do get your point about arity and parameter types. You're referring to the fact that not all functions can compose UNLESS they have compatible types and an arity of 1 in the parameters and an arity of 1 in the return value (Golang for example can have arity > 1 in the return value).

However this arity issue doesn't really exist. A function with Arity of two is isomorphic to a function of arity 1 that takes in a 2-tuple as input.

  f(a, b) -> c
  f'(Tuple[a, b]) -> c

  g(d) -> Tuple[a, b]
Both f' and f are equivalent in theory. Plus you can compose g with f'.

The only compatibility problems with composition among functions in the end is just types (not arity) but this problem still exists even in the unix world... it's just hidden from you because you don't actually watch the programs perform deserialization and serialization.
crimsonalucard5
·6 anni fa·discuss
> Just because the logic is organized as functions doesn’t magically make things fit together.

This is where you're a bit off. You're not technically wrong though. A function that outputs a string can't compose with a function that takes an int as a parameter, but this isn't what I'm referring to. Allow me to illustrate a shorter example.

Before we start off, we should both be in agreement that piping results from unix programs into other unix programs is a very lego-like method of programming, so if I can bend a program into following a similar style I've essentially "bent" the program into something that is "lego-like."

Say my programming task is to find out the value of (2 + 1) * 3 * 2. I will illustrate how to do this using a very typical functional style then using the point free style then using OOP and finally using unix pipes. The goal in every example is to promote reuse-ability to the maximum potential. With these examples you can develop the insight needed to see why functional programming is more lego like than other forms of programming.

functional style:

  f :: int -> int
  f x = (x + 1) * 3 * 2

  main = print (f 2)
The above code is your typical functional program. Coded in a way that's not apparently re-useable at first. However, I look at this function f and I realize I can easily begin refactoring it piece by piece into something more re-useable / lego-like. Take "* 2" let's extract that first into a function called g:

  g :: int -> int
  g x = x * 2 

  f :: int -> int
  f x = g ((x + 1) * 3)

  main = print (f 2)
Then see "* 3" it's very easy to see how you can extract that into another function called d:

  g :: int -> int
  g x = x * 2 

  d :: int -> int
  d x = x * 3

  f :: int -> int
  f x = d (g (x + 1))

  main = print (f 2)
Then see "+ 1". Again easily extracted into a new function called m.

  g :: int -> int
  g x = x * 2 

  d :: int -> int
  d x = x * 3

  m :: int -> int
  m x = x + 1

  f :: int -> int
  f x = d (g (m x))

  main = print (f 2) 
Pretty re-useable right? Let's rewrite the function "f" so that this becomes more apparent using the point free style:

  g :: int -> int
  g x = x * 2 

  d :: int -> int
  d x = x * 3

  m :: int -> int
  m x = x + 1

  f :: int -> int
  f = d . g . m

  main = print (f 2) 
Note that now it becomes obvious. The function f is a composition of d and g and m. It fits to together like legos. Now let us consider the same thing but with unix pipes. How would that work? Imagine you have three programs each one takes an input from stdin does a mathematical operation in them and throws the result to stdout. Each of these programs is named after the functions shown above and given the same mathematical operations as defined above: g, d, and m. What does the unix expression look like?

  $>  echo 2 | m | g | d
So you see I've given you a very primitive example using very basic mathematical operations to show you how easily it is to convert functional code into unix-like code which in turn is very lego-like.

I chose a simple example and even then you may not realize it now but if your program follows the rules of functional programming even when it scales it will have the properties that I just illustrated to you.

Now let's talk about OOP. The primitive and canonical solution to OOP cannot be decomposed into anything that is re-useable without massive changes to the structure of the code. Even for the simplest cases.

Allow me again to elucidate by converting g, d, and m into the canonical OOP code:

  class F:
    def __ init__(self, x)
        self.x = x
    def addOne(self)
        self.x += 1
    def mulTwo(self)
        self.x *= 2
    def mulThree(self)
        self.x *= 3

  f = F(2)
  f.addOne()
  f.mulTwo()
  f.mulThree()

  print (f.x)
The act of mutation ties methods to context and makes it so methods cannot be reused outside of a context. None of those methods can be reused outside of the context of F. If you want to make OOP into lego building blocks you're going to have to divide things up into smaller classes. Which is not nearly as straightforward as the functional program above.

Ultimately you'll realize that you're going to be dividing your class into smaller classes and the context becomes less and less relevant until you have something isomorphic to functional programming. Let's try making the above re-useable.

Let's try taking out mulTwo and making that reuseable.

    class F:
      def __ init__(self, x)
          self.x = x
      def addOne(self)
          self.x += 1
      def mulThree(self)
          self.x *= 3

   class G:
      def __ init__(self, x)
          self.x = x
      def mulTwo(self)
          self.x *= 2

  


  f = F(g)
  f.addOne()
  f.mulThree()
  g = G(f.x)
  g.mulTwo()
  print(g.x)
Now for mulThree and addOne:

    class F:
      def __ init__(self, x)
          self.x = x
      def addOne(self)
          self.x += 1

   class G:
      def __ init__(self, x)
          self.x = x
      def mulTwo(self)
          self.x *= 2
   
   class D:
      def __ init__(self, x)
          self.x = x
      def mulThree(self)
          self.x *= 2

  f = F(g)
  f.addOne()
  g = G(f.x)
  g.mulTwo()
  d = D(g.x)
  d.mulThree()
  
  print(d.x)
You will note that I'm reusing the constructor, How can that be factored out? Inheritance.

    class Base:
      def __ init__(self, x)
          self.x = x

    class F(Base):
      def addOne(self)
          self.x += 1

   class G(Base):
      def mulTwo(self)
          self.x *= 2
   
   class D(Base):
      def mulThree(self)
          self.x *= 2

  f = F(g)
  f.addOne()
  g = G(f.x)
  g.mulTwo()
  d = D(g.x)
  d.mulThree()
  
  print(d.x)

So let's look at things from a birds eye view. With the functional style I was able to refactor to 3 primitives all of which can be reused and composed.

OOP I was able to refactor it into 4 primitives with one primitive (Base) ultimately being useless outside of the context of this problem and 3 other primitives that rely on base as a parent. Additionally you will note that I need 6 lines of code to achieve the computation I need and the compositions hardly look lego like because they involve initializations and mutations.

Literally for even a simple simple example OOP breaks down. You cannot make it lego-like WITHOUT using functional concepts. Literally the next intuitive step if you were doing OOP is to write this:

  class F:
     def addOne(x):
         return x + 1
Which is heading in a more functional direction as the class just becomes useless sugar around a method that ignores internal state and is basically a function.

Also let's keep this discussion civil. I spent a lot of time writing this out for you. I will read and respond to your response but if I read anything like "You're niave" or "your opinions are shallow" you can guarantee I'm not going to continue any form of discussion with you.
crimsonalucard5
·6 anni fa·discuss
>The goal of OOP is also to be modular and composable.

Well it depends on the type of composition you're talking about. Anything when mashed together hard enough can compose.
crimsonalucard5
·6 anni fa·discuss
Unless you use some stateful program or you're writing to a file typically a unix expression that uses purely reads and pipes is deterministic meaning you run the same expression twice you get the same result.

If you want to get pedantic fine, but the majority of use cases is stateless.
crimsonalucard5
·6 anni fa·discuss
Cool! Glad I was able to convince you. Just note that a lot of people find the functional style kind of unnecessarily mind bending. Like the functional style primarily uses recursion instead of loops for iteration which is harder to reason about (and less performant). I actually agree with them, but I feel that these people got lost in the details and missed the big picture about the benefits of functional programming from the perspective of modules, design and program organization.

I recommend you try building something complex with the point free style using Haskell. It will be a bit mind bending at first but if you get it then you'll see how FP is basically the same thing as using legos to build data pipelines... exactly the style used with unix pipes.
crimsonalucard5
·6 anni fa·discuss
I mean what he describes is literally the same thing as haskell. Just untyped.
crimsonalucard5
·6 anni fa·discuss
You can create building blocks of unlimited complexity or pure simplicity.

The key lies in your choice of primitives. Do you start off with a set primitives that are extremely complex or a set primitives that are simple but complete in the sense that you can build entities of unlimited complexity by composing simpler primitives?

I would say the latter method is easier. But there's no reason why the former method won't work. Biological evolution has built many examples of working machines with primitives of unimaginable complexity.
crimsonalucard5
·6 anni fa·discuss
>You can't just compose two functions because they're both written in a functional programming language.

Obviously not, the types have to be compatible just like how a lego piece must be compatible.

>The programmer has to have the foresight to make their types compatible.

Just like a lego builder needs to have the foresight to see whether two lego components are compatible. The analogy still fits.

> I think the novelty of the Unix pipe for interoperability is that they (typically) work on one agreed-upon kind of data: human readable, white-space separated. So a lot of tools "just work" with each other.

The agreed upon data is just a type. Unix pipes compose functions that take in strings as input and deliver strings as output.

The spaces is just an oversight, the whole thing would have been more clean if the type was designed to represent multiple pieces of data like a tuple of words rather than a single string with words divided by spaces.

>you could certainly fail to do this with functional programming.

Just like how two lego pieces can't compose if they're not compatible pieces facing the right direction. Sockets and plugs must be compatible. A functional program still fits the analogy of legos perfectly.

Take a look at lego pieces here: https://brickarchitect.com/2019/2019-most-common-lego-parts/

There exists lego parts that can never compose with certain other parts. Just like functions and function composition.

What you're not seeing is how other styles of programming such as OOP or procedural programming fail to fit the analogy and literally become like organ transplantation.

Let's take a look at object composition. How does that work in the context of legos? It's like a lego block with a mutating hole in it and once you can put different things in that hole and that changes the overall lego block.

Also with OOP and procedural programming you get lego blocks that can mutate. Lego blocks can transform themselves and other lego blocks. They form an interconnected network of entities that are constantly mutating.

To fit the analogy of a lego block you need unchanging blocks.

The entire field of Procedural programming and OOP is basically the art of building systems with mutating primitives that constantly change. Imagine constructing buildings using bricks that change shape. Now imagine refactoring code that does this.... Organ transplantation.

Also you really just need to try it. Doing the type of grafting and refactoring that's an intrinsic part of programming literally becomes lego-like once you program using the functional style. To really see the big picture I suggest you exclusively try the point free style using Haskell.

That being said. There are still massive tradeoffs to doing functional programming. But if your goal is to program like you're using lego blocks, functional programming is the path.
crimsonalucard5
·6 anni fa·discuss
Unix piping is basically functional programming.

If you ever wondered why some people are obsessed with functional programming this is the reason why:

Functional programming forces every primitive in your program to be a Lego Block.

A lot of functional programmers don't see the big picture. They see a sort of elegance with the functional style, they like the immutability but they can't explain the practical significance to the uninitiated.

Functional Programming is the answer to the question that has plagued me as a programmer for years. How do I organize my program in such a way that it becomes endlessly re-useable from a practical standpoint?

Functional programming transforms organ transplantation into lego building blocks.

The "lego" is the "function" and "connecting two lego blocks" is "function composition".

In short, another name for "Point free style" programming is "Lego building block style" programming.
crimsonalucard5
·6 anni fa·discuss
Have you guys heard of functional programming?

Have you guys wondered why some people are so fanatic about functional programming?

Functional programming forces your program to be lego blocks by Definition.

A function with no side effects IS A Lego Block.

If I never discovered functional programming I'd be like John D. Cook, blogging about how programming is like organ transplantation.

Functional Programming is the answer to the question that has plagued me as a programmer for years: How do I organize/design my program in such a way that it becomes endlessly re-useable from a practical standpoint?

If this question plagues you as a programmer I urge you to take a closer look at functional programming. Specifically look at functions and function composition.