Programming

하스켈에 대한 소란은 무엇입니까?

procodes 2020. 8. 4. 20:10
반응형

하스켈에 대한 소란은 무엇입니까? [닫은]


나는 Haskell이 그들 사이에있을 때 이야기를 계속하는 몇 명의 프로그래머를 알고 있습니다. 여기서 모든 사람들이 그 언어를 좋아하는 것 같습니다. 하스켈을 잘하는 것은 천재 프로그래머의 특징과 비슷합니다.

누군가가 왜 그렇게 우아하고 우수한지를 보여주는 몇 가지 Haskell 예제를 줄 수 있습니까?


그것이 한 달 동안 Haskell에 대해 학습 한 후에 제가 생각한 사실은 기능 프로그래밍이 두뇌를 흥미로운 방식으로 비틀어 놓는 것입니다. 다른 방식으로 익숙한 문제에 대해 생각하게합니다. : 루프 대신 맵, 폴드 및 필터 등을 생각하십시오. 일반적으로 문제에 대한 관점이 둘 이상인 경우 일반적으로이 문제에 대한 추론을 가능하게하고 필요에 따라 관점을 전환 할 수 있습니다.

Haskell의 또 다른 멋진 점은 유형 시스템입니다. 엄격하게 입력되었지만 유형 유추 엔진은 바보 같은 유형 관련 실수를했을 때 마술처럼 알려주는 Python 프로그램처럼 느껴집니다. 이와 관련하여 Haskell의 오류 메시지는 다소 부족하지만 언어에 익숙해지면 스스로에게 말할 것입니다 : 이것이 타이핑이되어야하는 것입니다!


이것은 Haskell을 배우도록 설득 예입니다 (그리고 소년은 내가 기뻤습니다).

-- program to copy a file --
import System.Environment

main = do
         --read command-line arguments
         [file1, file2] <- getArgs

         --copy file contents
         str <- readFile file1
         writeFile file2 str

좋습니다. 짧고 읽기 쉬운 프로그램입니다. 그런 의미에서 C 프로그램보다 낫습니다. 그러나 이것은 매우 유사한 구조를 가진 파이썬 프로그램과 어떻게 다른가?

답은 게으른 평가입니다. 대부분의 언어 (일부 기능적 언어)에서 위와 같은 구조의 프로그램은 전체 파일을 메모리에로드 한 다음 새로운 이름으로 다시 작성합니다.

하스켈은 "게으르다". 그것은 필요할 때까지 물건을 계산하지 않으며, 확장하여 필요없는 것을 계산 하지 않습니다 . 예를 들어, writeFile을 제거하면 Haskell은 처음부터 파일에서 아무것도 읽지 않아도됩니다.

따라서 Haskell은에 writeFile따라 달라 지므로이 readFile데이터 경로를 최적화 할 수 있습니다.

결과는 컴파일러에 따라 다르지만 위의 프로그램을 실행할 때 일반적으로 발생하는 일은 다음과 같습니다. 프로그램은 첫 번째 파일의 블록 (예 : 8KB)을 읽은 다음 두 번째 파일에 쓴 다음 첫 번째 파일에서 다른 블록을 읽습니다. 파일을 작성하고 두 번째 파일 등에 씁니다. (실행 해보 strace십시오!)

... 파일 복사의 효율적인 C 구현이하는 것과 매우 비슷합니다.

따라서 Haskell을 사용하면 간결하고 읽기 쉬운 프로그램을 작성할 수 있습니다.

내가 추가해야 할 또 다른 것은 Haskell이 단순히 버그가있는 프로그램을 작성하는 것을 어렵게한다는 것입니다. 놀라운 타입 시스템, 부작용의 부재, 그리고 Haskell 코드의 소형화는 최소한 세 가지 이유로 버그를 줄입니다.

  1. 더 나은 프로그램 디자인. 복잡성을 줄이면 로직 오류가 줄어 듭니다.

  2. 컴팩트 코드. 버그가 줄어 듭니다.

  3. 컴파일 오류 많은 버그 가 유효하지 않은 Haskell 입니다.

하스켈은 모든 사람을위한 것이 아닙니다. 그러나 누구나 시도해보십시오.


당신은 잘못된 질문을하고 있습니다.

하스켈은 몇 가지 멋진 예제에서 살펴 가서가는 언어가 아닙니다 "아하, 내가 지금 참조 입니다 그것은 좋은 만드는 것!"

더 비슷하게, 우리는 다른 프로그래밍 언어를 모두 가지고 있으며, 그것들은 다소 비슷합니다. 그리고 Haskell이 있습니다. 그러나 문제는 악취에 적응하는 데 꽤 오랜 시간이 걸린다는 것입니다. 하스켈을 거의 다른 짝수 반 주류 언어와 차별화시키는 것들 :

  • 게으른 평가
  • 부작용 없음 (모든 것이 순수하고 IO 등은 모나드를 통해 발생합니다)
  • 믿을 수 없을 정도로 표현적인 정적 유형 시스템

뿐만 아니라 많은 주류 언어와 다른 몇 가지 측면 (그러나 일부는 공유) :

  • 기능의
  • 중요한 공백
  • 유추 된 유형

다른 포스터가 대답했듯이 이러한 모든 기능의 조합은 프로그래밍에 대해 완전히 다른 방식으로 생각한다는 것을 의미합니다. 따라서 이것을 Joe-mainstream 프로그래머에게 적절하게 전달하는 예제 (또는 예제 세트)를 생각해 내기가 어렵습니다. 경험적인 것입니다. (비유하기 위해 1970 년 중국 여행의 사진을 보여줄 수 있지만 사진을 본 후에도 그 기간 동안 그곳에 살았던 것이 무엇인지 알 수 없습니다. 마찬가지로 하스켈을 보여 드릴 수 있습니다. 'quicksort'이지만 여전히 Haskeller가 무엇을 의미하는지 알 수 없습니다.)


실제로 Haskell을 차별화하는 것은 기능적 프로그래밍을 시행하기 위해 디자인을 시도하는 노력입니다. 거의 모든 언어로 기능적 스타일로 프로그래밍 할 수 있지만 첫 번째 편의에서 포기하기는 너무 쉽습니다. Haskell은 함수형 프로그래밍을 포기할 수 없으므로 논리적 인 결론에 도달해야합니다. 논리적 인 결론은 추론하기 쉬운 최종 프로그램이며 가장 까다로운 유형의 버그에 대한 전체 클래스를 회피합니다.

실제 사용을위한 프로그램 작성과 관련하여 Haskell에는 실용적인 방식이 부족할 수 있지만 최종 솔루션은 Haskell을 처음 시작하는 것이 좋습니다. 나는 아직 거기에 없지만, 지금까지 Haskell을 배우는 것은 Lisp가 대학에 있다고 말하는 것보다 훨씬 더 깨달았습니다.


Part of the fuss is that purity and static typing enable for parallelism combined with aggressive optimisations. Parallel languages are hot now with multicore being a bit disruptive.

Haskell gives you more options for parallelism than pretty much any general purpose language, along with a fast, native code compiler. There is really no competition with this kind of support for parallel styles:

So if you care about making your multicore work, Haskell has something to say. A great place to start is with Simon Peyton Jones' tutorial on parallel and concurrent programming in Haskell.


I've spent the last year learning Haskell and writing a reasonably large and complex project in it. (The project is an automated options trading system, and everything from the trading algorithms to the parsing and handling of low-level, high-speed market data feeds is done in Haskell.) It's considerably more concise and easier to understand (for those with appropriate background) than a Java version would be, as well as extremely robust.

Possibly the biggest win for me has been the ability to modularize control flow through things such as monoids, monads, and so on. A very simple example would be the Ordering monoid; in an expression such as

c1 `mappend` c2 `mappend` c3

where c1 and so on return LT, EQ or GT, c1 returning EQ causes the expression to continue, evaluating c2; if c2 returns LT or GT that's the value of the whole, and c3 is not evaluated. This sort of thing gets considerably more sophisticated and complex in things like monadic message generators and parsers where I may be carrying around different types of state, have varying abort conditions, or may want to be able to decide for any particular call whether abort really means "no further processing" or means, "return an error at the end, but carry on processing to collect further error messages."

This is all stuff it takes some time and probably quite some effort to learn, and thus it can be hard to make a convincing argument for it for those who don't already know these techniques. I think that the All About Monads tutorial gives a pretty impressive demonstration of one facet of this, but I wouldn't expect that anybody not familiar with the material already would "get it" on the first, or even the third, careful reading.

Anyway, there's lots of other good stuff in Haskell as well, but this is a major one that I don't see mentioned so often, probably because it's rather complex.


Software Transactional Memory is a pretty cool way to deal with concurrency. It's much more flexible than message passing, and not deadlock prone like mutexes. GHC's implementation of STM is considered one of the best.


For an interesting example you can look at: http://en.literateprograms.org/Quicksort_(Haskell)

What is interesting is to look at the implementation in various languages.

What makes Haskell so interesting, along with other functional languages, is the fact that you have to think differently about how to program. For example, you will generally not use for or while loops, but will use recursion.

As is mentioned above, Haskell and other functional languages excel with parallel processing and writing applications to work on multi-cores.


I couldn't give you an example, I'm an OCaml guy, but when I'm in such a situation as yourself, curiosity just takes hold and I have to download a compiler/interpreter and give it a go. You'll likely learn far more that way about the strengths and weaknesses of a given functional language.


One thing I find very cool when dealing with algorithms or mathematical problems is Haskell's inherent lazy evaluation of computations, which is only possible due to its strict functional nature.

For example, if you want to calculate all primes, you could use

primes = sieve [2..]
    where sieve (p:xs) = p : sieve [x | x<-xs, x `mod` p /= 0]

and the result is actually an infinite list. But Haskell will evaluate it left from right, so as long as you don't try to do something that requires the entire list, you can can still use it without the program getting stuck in infinity, such as:

foo = sum $ takeWhile (<100) primes

which sums all primes less than 100. This is nice for several reasons. First of all, I only need to write one prime function that generates all primes and then I'm pretty much ready to work with primes. In an object-oriented programming language, I would need some way to tell the function how many primes it should compute before returning, or emulate the infinite list behavior with an object. Another thing is that in general, you end up writing code that expresses what you want to compute and not in which order to evaluate things - instead the compiler does that for you.

This is not only useful for infinite lists, in fact it gets used without you knowing it all the time when there is no need to evaluate more than necessary.


I agree with others that seeing a few small examples is not the best way to show off Haskell. But I'll give some anyway. Here's a lightning-fast solution to Euler Project problems 18 and 67, which ask you to find the maximum-sum path from the base to the apex of a triangle:

bottomUp :: (Ord a, Num a) => [[a]] -> a
bottomUp = head . bu
  where bu [bottom]     = bottom
        bu (row : base) = merge row $ bu base
        merge [] [_] = []
        merge (x:xs) (y1:y2:ys) = x + max y1 y2 : merge xs (y2:ys)

Here is a complete, reusable implementation of the BubbleSearch algorithm by Lesh and Mitzenmacher. I used it to pack large media files for archival storage on DVD with no waste:

data BubbleResult i o = BubbleResult { bestResult :: o
                                     , result :: o
                                     , leftoverRandoms :: [Double]
                                     }
bubbleSearch :: (Ord result) =>
                ([a] -> result) ->       -- greedy search algorithm
                Double ->                -- probability
                [a] ->                   -- list of items to be searched
                [Double] ->              -- list of random numbers
                [BubbleResult a result]  -- monotone list of results
bubbleSearch search p startOrder rs = bubble startOrder rs
    where bubble order rs = BubbleResult answer answer rs : walk tries
            where answer = search order
                  tries  = perturbations p order rs
                  walk ((order, rs) : rest) =
                      if result > answer then bubble order rs
                      else BubbleResult answer result rs : walk rest
                    where result = search order

perturbations :: Double -> [a] -> [Double] -> [([a], [Double])]
perturbations p xs rs = xr' : perturbations p xs (snd xr')
    where xr' = perturb xs rs
          perturb :: [a] -> [Double] -> ([a], [Double])
          perturb xs rs = shift_all p [] xs rs

shift_all p new' [] rs = (reverse new', rs)
shift_all p new' old rs = shift_one new' old rs (shift_all p)
  where shift_one :: [a] -> [a] -> [Double] -> ([a]->[a]->[Double]->b) -> b
        shift_one new' xs rs k = shift new' [] xs rs
          where shift new' prev' [x] rs = k (x:new') (reverse prev') rs
                shift new' prev' (x:xs) (r:rs) 
                    | r <= p    = k (x:new') (prev' `revApp` xs) rs
                    | otherwise = shift new' (x:prev') xs rs
                revApp xs ys = foldl (flip (:)) ys xs

I'm sure this code looks like random gibberish. But if you read Mitzenmacher's blog entry and understand the algorithm, you'll be amazed that it's possible to package the algorithm into code without saying anything about what you're searching for.

Having given you some examples as you asked for, I will say that the best way to start to appreciate Haskell is to read the paper that gave me the ideas I needed to write the DVD packer: Why Functional Programming Matters by John Hughes. The paper actually predates Haskell, but it brilliantly explains some of the ideas that make people like Haskell.


For me, the attraction of Haskell is the promise of compiler guaranteed correctness. Even if it is for pure parts of the code.

I have written a lot of scientific simulation code, and have wondered so many times if there was a bug in my prior codes, which could invalidate a lot of current work.


I find that for certain tasks I am incredibly productive with Haskell.

The reason is because of the succinct syntax and the ease of testing.

This is what the function declaration syntax is like:

foo a = a + 5

That's is simplest way I can think of defining a function.

If I write the inverse

inverseFoo a = a - 5

I can check that it is an inverse for any random input by writing

prop_IsInverse :: Double -> Bool
prop_IsInverse a = a == (inverseFoo $ foo a)

And calling from the command line

jonny@ubuntu: runhaskell quickCheck +names fooFileName.hs

Which will check that all the properties in my file are held, by randomly testing inputs a hundred times of so.

I don't think Haskell is the perfect language for everything, but when it comes to writing little functions and testing, I haven't seen anything better. If your programming has a mathematical component this is very important.


If you can wrap your head around the type system in Haskell I think that in itself is quite an accomplishment.


it has no loop constructs. not many languages have this trait.


I agree with those that said that functional programming twists your brain into seeing programming from a different angle. I've only used it as a hobbyist, but I think it fundamentally changed the way I approach a problem. I don't think I would have been nearly as effective with LINQ without having been exposed to Haskell (and using generators and list comprehensions in Python).


To air a contrarian view: Steve Yegge writes that Hindely-Milner languages lack the flexibility required to write good systems:

H-M is very pretty, in a totally useless formal mathematical sense. It handles a few computation constructs very nicely; the pattern matching dispatch found in Haskell, SML and OCaml is particularly handy. Unsurprisingly, it handles some other common and highly desirable constructs awkwardly at best, but they explain those scenarios away by saying that you're mistaken, you don't actually want them. You know, things like, oh, setting variables.

Haskell is worth learning, but it has its own weaknesses.

참고URL : https://stackoverflow.com/questions/775726/whats-the-fuss-about-haskell

반응형