developer tip

함수형 프로그래밍에서 "점없는"스타일의 장점과 단점은 무엇입니까?

copycodes 2020. 11. 14. 10:44
반응형

함수형 프로그래밍에서 "점없는"스타일의 장점과 단점은 무엇입니까?


일부 언어 (Haskell?)에서는 점없는 스타일을 달성하거나 이름으로 함수 인수를 명시 적으로 참조하지 않는 것이 노력한다는 것을 알고 있습니다. 이것은 내가 마스터하기 매우 어려운 개념이지만, 그 스타일의 장점 (또는 단점)이 무엇인지 이해하는 데 도움이 될 수 있습니다. 누구든지 설명 할 수 있습니까?


나는 그 목적이 간결하고 인수를 스레딩 하는 것을 생각하기보다는 함수의 구성으로 파이프 라인 계산을 표현하는 것이라고 믿는다 . 간단한 예 (F #)-주어진 :

let sum = List.sum
let sqr = List.map (fun x -> x * x)

다음과 같이 사용 :

> sum [3;4;5]
12
> sqr [3;4;5]
[9;16;25]

"제곱합"함수를 다음과 같이 표현할 수 있습니다.

let sumsqr x = sum (sqr x)

그리고 다음과 같이 사용하십시오.

> sumsqr [3;4;5]
50

또는 x를 다음과 같이 파이핑하여 정의 할 수 있습니다.

let sumsqr x = x |> sqr |> sum

이 방법을 서면, 그것은 x가 전달되고 있음을 분명 단지 함수의 순서를 "스레드"합니다. 직접 구성이 훨씬 더 멋지게 보입니다.

let sumsqr = sqr >> sum

이것은 더 간결하고 우리가하는 일에 대한 다른 생각 방식입니다. 논쟁이 흐르는 과정을 상상하기보다는 함수를 구성합니다. sumsqr작동 방식을 설명하지 않습니다 . 우리는 무엇을 설명하고 있다 .

추신 : 컴포지션을 이해하는 흥미로운 방법은 Forth, Joy, Factor 등과 같은 연결 언어로 프로그래밍을 시도하는 것입니다. 이들은 : sumsqr sqr sum ;단어 사이의 공백이있는 컴포지션 (Forth ) 일 뿐이라고 생각할 수 있습니다 . 구성 연산자 .

PPS : 아마도 다른 사람들이 성능 차이에 대해 언급 할 수있을 것입니다. 컴포지션은 파이프 라이닝에서와 같이 중간 값을 생성 할 필요가 없음을 컴파일러에게 분명 하게함으로써 GC 압력을 줄일 수있는 것 같습니다 . 소위 "삼림 벌채"문제를 더 다루기 쉽게 만드는 데 도움이됩니다.


일부 저자는 포인트 프리 스타일을 궁극적 인 함수형 프로그래밍 스타일 로 간주합니다 . 간단히 말해, 유형의 함수는 유형의 t1 -> t2한 요소에서 유형의 t1다른 요소로 의 변환을 설명합니다 t2. 아이디어는 "지시적"함수 (명시 적 변수를 사용하여 작성 됨)가 요소를 강조하고 (를 작성할 때 요소에\x -> ... x ... 무슨 일이 일어나고 있는지 설명하는 것임 x) 반면에 "포인트없는"함수 (변수를 사용하지 않고 표현됨)가 변환을 강조한다는 것입니다.더 간단한 변환의 구성으로 그 자체. 무점 스타일을 옹호하는 사람들은 변환이 실제로 중심 개념이어야하며, 사용하기 쉽지만 지적 표기법은이 고귀한 이상에서 우리를 산만하게한다고 주장합니다.

포인트 프리 함수형 프로그래밍은 오랫동안 사용 가능했습니다. 이것은 1924 년 Moses Schönfinkel의 주요 연구 이후 조합 논리를 연구 한 논리 학자에 의해 이미 알려 졌으며 1950 년대 Robert Feys와 Haskell Curry 가 ML 유형 추론이 될 첫 번째 연구의 기초가되었습니다 .

표현적인 기본 결합기 세트에서 함수를 빌드하는 아이디어는 매우 매력적이며 APL 에서 파생 된 배열 조작 언어 또는 Haskell의 Parsec 과 같은 파서 결합기 라이브러리와 같은 다양한 도메인에 적용되었습니다 . 포인트 프리 프로그래밍의 주목할만한 옹호자는 John Backus 입니다. 1978 년 연설에서 "프로그래밍은 폰 노이만 스타일에서 해방 될 수 있는가?"에서 다음과 같이 썼습니다.

람다 식 (대체 규칙 포함)은 가능한 모든 유형 및 인수의 모든 가능한 계산 가능한 함수를 정의 할 수 있습니다. 이 자유와 권력은 분명한 장점뿐만 아니라 단점도 가지고 있습니다. 이는 기존 언어에서 제한되지 않은 제어문의 힘과 유사합니다. 제한없는 자유로 인해 혼란이 발생합니다. 람다 미적분학에서 할 수있는 것처럼 상황에 맞는 새로운 결합 형태를 끊임없이 발명한다면 모든 목적에 적합한 몇 가지 결합 형태의 스타일이나 유용한 속성에 익숙해지지 않을 것입니다. 구조화 된 프로그래밍이 더 단순한 구조, 더 나은 속성 및 동작 이해를위한 균일 한 방법을 가진 프로그램을 얻기 위해 많은 제어 문을 피하는 것처럼 함수형 프로그래밍은 람다 식, 대체, 및 여러 기능 유형. 따라서 알려진 유용한 속성을 가진 친숙한 기능적 형식으로 빌드 된 프로그램을 얻을 수 있습니다. 이러한 프로그램은 매우 구조화되어있어 고등학교 대수 문제를 해결하는 데 사용되는 것과 유사한 대수 기술을 기계적으로 사용하여 행동을 이해하고 입증 할 수 있습니다.

그래서 여기 있습니다. 무점 프로그래밍의 주요 장점은 방정식 추론을 자연스럽게 만드는 구조화 된 결합 자 스타일을 강요한다는 것입니다. 방정식 추론은 특히 "Squiggol"운동의 지지자들에 의해 광고되었으며 ([1] [2] 참조) 실제로 점없는 결합 자와 계산 / 재 작성 / 추론 규칙을 상당히 많이 사용합니다.

Finally, one cause for the popularity of point-free programming among Haskellites is its relation to category theory. In category theory, morphisms (which could be seen as "transformations between objects") are the basic object of study and computation. While partial results allow reasoning in specific categories to be performed in a pointful style, the common way to build, examine and manipulate arrows is still the point-free style, and other syntaxes such as string diagrams also exhibit this "pointfreeness". There are rather tight links between the people advocating "algebra of programming" methods and users of categories in programming (for example the authors of the banana paper [2] are/were hardcore categorists).

You may be interested in the Pointfree page of the Haskell wiki.

The downside of pointfree style is rather obvious: it can be a real pain to read. The reason why we still love to use variables, despite the numerous horrors of shadowing, alpha-equivalence etc., is that it's a notation that's just so natural to read and think about. The general idea is that a complex function (in a transparently referential language) is like a complex plumbing system: the inputs are the parameters, they get into some pipes, are applied to inner functions, duplicated (\x -> (x,x)) or forgotten (\x -> (), pipe leading nowhere), etc. And the variable notation is nicely implicit about all that machinery: you give a name to the input, and names on the outputs (or auxiliary computations), but you don't have to describe all the plumbing plan, where the small pipes will go not to be a hindrance for the bigger ones, etc. The amount of plumbing inside something as short as \(f,x,y) -> ((x,y), f x y) is amazing. You may follow each variable individually, or read each intermediate plumbing node, but you never have to see the whole machinery together. When you use a point-free style, it's all explicit, you have to write everything down, and look at it afterwards, and sometimes it's just plain ugly.

PS: this plumbing vision is closely related to the stack programming languages, which are probably the least pointful programming languages (barely) in use. I would recommend trying to do some programming in them just to get of feeling of it (as I would recommend logic programming). See Factor, Cat or the venerable Forth.


While I'm attracted to the point-free concept and used it for some things, and agree with all the positives said before, I found these things with it as negative (some are detailed above):

  1. The shorter notation reduces redundancy; in a heavily structured composition (ramda.js style, or point-free in Haskell, or whatever concatenative language) the code reading is more complex than linearly scanning through a bunch of const bindings and using a symbol highlighter to see which binding goes into what other downstream calculation. Besides the tree vs linear structure, the loss of descriptive symbol names makes the function hard to intuitively grasp. Of course both the tree structure and the loss of named bindings also have a lot of positives as well, for example, functions will feel more general - not bound to some application domain via the chosen symbol names - and the tree structure is semantically present even if bindings are laid out, and can be comprehended sequentially (lisp let/let* style).

  2. Point-free is simplest when just piping through or composing a series of functions, as this also results in a linear structure that we humans find easy to follow. However, threading some interim calculation through multiple recipients is tedious. There are all kinds of wrapping into tuples, lensing and other painstaking mechanisms go into just making some calculation accessible, that would otherwise be just the multiple use of some value binding. Of course the repeated part can be extracted out as a separate function and maybe it's a good idea anyway, but there are also arguments for some non-short functions and even if it's extracted, its arguments will have to be somehow threaded through both applications, and then there may be a need for memoizing the function to not actually repeat the calculation. One will use a lot of converge, lens, memoize, useWidth etc.

  3. JavaScript specific: harder to casually debug. With a linear flow of let bindings, it's easy to add a breakpoint wherever. With the point-free style, even if a breakpoint is somehow added, the value flow is hard to read, eg. you can't just query or hover over some variable in the dev console. Also, as point-free is not native in JS, library functions of ramda.js or similar will obscure the stack quite a bit, especially with the obligate currying.

  4. Code brittleness, especially on nontrivial size systems and in production. If a new piece of requirement comes in, then the above disadvantages get into play (eg. harder to read the code for the next maintainer who may be yourself a few weeks down the line, and also harder to trace the dataflow for inspection). But most importantly, even something seemingly small and innocent new requirement can necessitate a whole different structuring of the code. It may be argued that it's a good thing in that it'll be a crystal clear representation of the new thing, but rewriting large swaths of point-free code is very time consuming and then we haven't mentioned testing. So it feels that the looser, less structured, lexical assignment based coding can be more quickly repurposed. Especially if the coding is exploratory, and in the domain of human data with weird conventions (time etc.) that can rarely be captured 100% accurately and there may always be an upcoming request for handling something more accurately or more to the needs of the customer, whichever method leads to faster pivoting matters a lot.

참고URL : https://stackoverflow.com/questions/5671271/what-are-advantages-and-disadvantages-of-point-free-style-in-functional-progra

반응형