Elm 주장과 같이 "런타임 예외 없음"의 이점은 무엇입니까?


16

일부 언어는 다른 언어에 비해 "런타임 예외 없음"이라고 분명한 장점이 있다고 주장합니다.

나는 그 문제에 혼란스러워한다.

런타임 예외는 내가 아는 한 잘 사용되는 도구 일뿐입니다.

  • "더러운"상태를 전달할 수 있습니다 (예기치 않은 데이터 발생)
  • 스택을 추가하면 오류 체인을 가리킬 수 있습니다
  • 클러 터 (예 : 유효하지 않은 입력에서 빈 값 반환)와 개발자의주의가 필요한 안전하지 않은 사용 (예 : 유효하지 않은 입력에서 예외 발생)을 구분할 수 있습니다.
  • 디버깅 노력에 도움이되는 유용한 정보를 제공하는 예외 메시지를 통해 오류에 세부 정보를 추가 할 수 있습니다 (이론적으로)

반면에 나는 예외를 "삼키는"소프트웨어를 디버깅하기가 정말 어렵다는 것을 알게되었다. 예 :

try { 
  myFailingCode(); 
} catch {
  // no logs, no crashes, just a dirty state
}

따라서 "런타임 예외가 없다"는 강력한 이론적 장점은 무엇입니까?


https://guide.elm-lang.org/

실제로 런타임 오류가 없습니다. null이 없습니다. 정의되지 않은 것은 함수가 아닙니다.


나는 당신이 언급하고있는 언어 중 하나 또는 두 가지 예를 제공하고 그러한 주장에 대한 링크를 제공하는 것이 도움이 될 것이라고 생각합니다. 이것은 여러 가지 방법으로 해석 될 수 있습니다.
JimmyJames

내가 찾은 최신 예제는 C, C ++, C #, Java, ECMAScript 등과 비교하여 elm입니다. @JimmyJames
atoth

2
이것이 내가 처음 들었습니다. 나의 첫 반응은 BS에 전화하는 것입니다 족제비 단어에 주목 : 실제로
JimmyJames

@atoth 저는 질문 제목을보다 명확하게하기 위해 질문 제목을 편집하려고합니다. Java와 유사한 "RuntimeException"과 "Exception"과 같이 관련없는 여러 질문이 있기 때문입니다. 새 제목이 마음에 들지 않으면 언제든지 다시 편집하십시오.
Andres F.

좋아, 나는 그것이 일반적인 것이기를 원했지만 도움이된다면 @AndresF의 기여에 감사드립니다.!
atoth December

답변:


28

예외는 의미를 극도로 제한합니다. 그것들은 그들이 던져진 곳이나 직접 호출 스택에서 위쪽으로 정확하게 처리되어야하며, 잊어 버린 경우 컴파일 타임에 프로그래머에게 표시되지 않습니다.

오류가 결과 또는 가능성으로 인코딩되는 Elm과 대조해보십시오 . 둘 다 입니다. 즉, 오류를 처리하지 않으면 컴파일러 오류가 발생합니다. 변수 나 컬렉션에 저장하여 편리한 시간에 처리를 연기 할 수 있습니다. 모든 곳에서 매우 유사한 try-catch 블록을 반복하는 대신 응용 프로그램 별 방식으로 오류를 처리하는 함수를 만들 수 있습니다. 모든 부분이 성공한 경우에만 성공하는 계산에 연결하여 하나의 try 블록에 넣을 필요가 없습니다. 내장 구문에 의해 제한되지 않습니다.

이것은 "예외 삼키기"와는 다릅니다. 유형 시스템에서 오류 조건을 명시 적으로 만들고 훨씬 더 유연한 대체 의미 체계를 제공하여 오류를 처리합니다.

다음 예를 고려하십시오. 실제로 보려면 http://elm-lang.org/try에 붙여 넣을 수 있습니다 .

import Html exposing (Html, Attribute, beginnerProgram, text, div, input)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import String

main =
  beginnerProgram { model = "", view = view, update = update }

-- UPDATE

type Msg = NewContent String

update (NewContent content) oldContent =
  content

getDefault = Result.withDefault "Please enter an integer" 

double = Result.map (\x -> x*2)

calculate = String.toInt >> double >> Result.map toString >> getDefault

-- VIEW

view content =
  div []
    [ input [ placeholder "Number to double", onInput NewContent, myStyle ] []
    , div [ myStyle ] [ text (calculate content) ]
    ]

myStyle =
  style
    [ ("width", "100%")
    , ("height", "40px")
    , ("padding", "10px 0")
    , ("font-size", "2em")
    , ("text-align", "center")
    ]

메모 String.toIntcalculate기능이 실패 할 가능성이있다. Java에서는 런타임 예외가 발생할 가능성이 있습니다. 사용자 입력을 읽을 때 상당히 가능성이 높습니다. Elm은 대신을 반환하여 처리하도록 강요 Result하지만 즉시 처리 할 필요는 없습니다. 입력을 두 배로하여 문자열로 변환 한 다음getDefault 함수 에서 입력이 잘못되었는지 확인할 수 있습니다. 이 위치는 오류가 발생한 지점이나 호출 스택에서 위쪽보다 점검에 훨씬 더 적합합니다.

컴파일러가 우리의 손을 강제하는 방식은 Java의 확인 된 예외보다 훨씬 세분화됩니다. Result.withDefault원하는 값을 추출하는 것과 같은 매우 특정한 기능을 사용해야합니다 . 기술적으로 이러한 종류의 메커니즘을 남용 할 수는 있지만 그다지 중요하지 않습니다. 좋은 기본 / 오류 메시지를 입력 할 때까지 결정을 연기 할 수 있으므로 사용하지 않을 이유가 없습니다.


8
That means you get a compiler error if you don't handle the error.Java의 Checked Exceptions에 대한 추론이지만, 우리는 그것이 얼마나 잘 작동했는지 알고 있습니다.
Robert Harvey

4
@RobertHarvey 어떤면에서 Java의 확인 된 예외는 가난한 사람의 버전이었습니다. 불행히도 (예제와 같이) "삼키는"것일 수 있습니다. 또한 실제 유형이 아니기 때문에 코드 흐름에 대한 추가 경로를 만들었습니다. 더 나은 타입 시스템과 언어는 (말)에서 할 것입니다 일류 값으로 인코딩 오류가있는 하스켈에 당신을 허용 Maybe, Either그것은 같은 등 느릅 나무의 모습은 ML, OCaml의 또는 하스켈 같은 언어에서 페이지를 복용.
Andres F.

3
@JimmyJames 아니오, 그들은 당신을 강요하지 않습니다. 값을 사용하려면 오류를 "처리"해야합니다. 내가 할 경우 x = some_func(), 나는하지 않습니다 난의 값 검사하려는 경우가 아니면 아무것도 할 x내가 오류 또는 "유효"값이 있는지 여부를 제가 확인할 수있는 경우를; 또한 하나를 다른 대신 사용하려고 시도하는 것은 정적 유형 오류이므로 사용할 수 없습니다. 느릅 나무의 종류가 다른 함수형 언어 같은 것을 작업하는 경우, 실제로 다른 기능에서 작성 값과 같은 물건을 할 수 있습니다 전에 나는 심지어 그들이 여부 오류가 있는지 알아! 이것은 전형적인 FP 언어입니다.
Andres F.

6
@atoth하지만 당신은 상당한 이득을 가지고있을 것입니다 아주 좋은 이유는 (로되어있는 한 귀하의 질문에 복수 응답에서 설명). ML과 같은 구문으로 언어를 배우는 것이 정말 좋습니다. C와 같은 구문 균열 (ML)은 70 년대 초반에 개발되어 언어를 얼마나 자유롭게 만드는지 대략적으로 알 수 있습니다. C의 현대). 이런 종류의 시스템을 설계 한 사람들은 C와 달리 이런 종류의 구문을 정상적인 것으로 간주합니다. :) 당신이 그것에있는 동안에도 리스프를 배우는 것이 아프지 않을 것입니다 :)
Andres F.

6
@atoth이 모든 것에서 한 가지를 가져 오려면이 중 하나를 가져 가십시오. 항상 Blub Paradox에 빠지지 않도록하십시오 . 새로운 구문에 짜증을 내지 마십시오. 어쩌면 그것은 당신이 익숙하지 않은 강력한 기능으로 인해있을 수 있습니다 :)
Andres F.

10

이 진술을 이해하려면 먼저 정적 유형 시스템이 우리를 구입하는 것을 이해해야합니다. 본질적으로, 정적 타입 시스템이 우리에게주는 무슨, 보증이다 IFF에 프로그램 유형 검사, 런타임 행동의 특정 클래스가 발생할 수 없습니다.

불길하게 들린다. 글쎄, 타입 체커는 정리 체커와 비슷합니다. (실제로, 커리 하워드 이소 형에 따르면 그것들은 똑같습니다.) 정리에 대해 매우 특이한 것은 정리를 증명할 때 정리가 말하는 것을 더 이상 증명하지 못한다는 것입니다. 예를 들어, 누군가 "이 프로그램이 올바른 것으로 입증되었습니다"라고 말할 때 항상 " '정확한 정의하십시오"라고 요청해야합니다.) 유형 시스템의 경우에도 마찬가지입니다. "프로그램이 형식에 안전합니다"라고 할 때 가능한 오류가 발생하지 않는다는 의미입니다 . 우리는 유형 시스템이 우리에게 예방할 것을 약속하는 오류가 발생할 수 없다고 말할 수 있습니다.

따라서 프로그램은 다양한 런타임 동작을 무한히 가질 수 있습니다. 그 중에서도 무한히 많은 것이 유용하지만, 무한히 많은 것도 "올바르지 않다"( "정확도"의 다양한 정의에 대해)입니다. 정적 유형 시스템을 사용하면 무한한 많은 잘못된 런타임 동작이 발생하는 유한하고 고정 된 특정 집합을 증명할 수 있습니다.

서로 다른 유형 시스템의 차이점은 기본적으로 어느 정도, 얼마나 많은, 얼마나 복잡한 런타임 동작이 발생하지 않는지에 있습니다. Java와 같은 약한 유형 시스템은 매우 기본적인 것만 증명할 수 있습니다. 예를 들어, Java는 a를 반환하는 것으로 입력 된 메서드가 a를 반환 할 String수 없음을 증명할 수 있습니다 List. 그러나, 예를 들어, 그것은 할 수 없는 방법은 반환하지 않습니다 것을 증명한다. 또한 메소드가 예외를 발생시키지 않음을 증명할 수 없습니다. 그리고는 반환하지 않습니다 것을 증명할 수 잘못 String  - 어느 String유형 검사를 만족시킬 것입니다. (물론, null그것을 만족시킬 수도 있습니다.) Java가 증명할 수없는 매우 간단한 것들이 있기 ArrayStoreException때문에 ClassCastException, 또는 모두가 좋아하는 NullPointerException.

Agda와 같은보다 강력한 형식 시스템은 "두 인수의 합을 반환합니다"또는 "인수로 전달 된 목록의 정렬 된 버전을 반환합니다"와 같은 것을 증명할 수 있습니다.

이제 Elm의 디자이너가 런타임 예외가 없다는 진술에 의해 의미하는 것은 Elm의 유형 시스템이 다른 언어 에서는 발생 하지 않는 것으로 입증 될 수 없는 런타임 동작의 부재가 있음을 증명할 수 있다는 것입니다. 런타임에 잘못된 동작 (가장 좋은 경우 예외를 의미하고, 최악의 경우 충돌을 의미하며, 최악의 경우 모두 충돌 없음, 예외 없음, 조용히 잘못된 결과)을 의미합니다.

그래서, 그들은되어 있지 "우리가 예외를 구현하지 않습니다"라고. 그들은 Elm에 온 전형적인 프로그래머들이 경험할 수있는 전형적인 언어에서 런타임 예외가 될 수있는 것들이 타입 시스템에 의해 잡히고 있다고 말합니다. 물론 Idris, Agda, Guru, Epigram, Isabelle / HOL, Coq 또는 이와 유사한 언어에서 온 사람은 Elm을 비교할 때 상당히 약한 것으로 간주합니다. 이 문장은 일반적인 Java, C♯, C ++, Objective-C, PHP, ECMAScript, Python, Ruby, Perl,… 프로그래머를 대상으로합니다.


5
잠재적 인 편집자 주 : 이중 및 삼중 네거티브 사용에 대해 매우 죄송합니다. 그러나 타입 시스템은 특정 종류의 런타임 동작이 없음을 보장합니다. 즉, 특정 상황이 발생하지 않도록 보장합니다. 그리고 "공식이 발생하지 않는 것으로 입증"된 그대로 유지하고 싶었지만 불행히도 "방법이 반환되지 않는다는 것을 증명할 수 없습니다"와 같은 구성으로 이어졌습니다. 이를 개선 할 수있는 방법을 찾으면 계속 진행하되 위의 사항을 명심하십시오. 감사합니다!
Jörg W Mittag

2
전반적으로 좋은 대답이지만 한 가지 작은 닉픽 : "타입 체커는 정리 증명 자와 유사합니다." 실제로 유형 체커는 정리 체커와 더 비슷합니다. 둘 다 공제가 아닌 검증을 수행 합니다 .
Gardenhead

4

Elm은 C가 런타임 예외를 보장하지 않는 것과 같은 이유로 런타임 예외를 보장하지 않습니다. 언어는 예외 개념을 지원하지 않습니다.

Elm은 런타임시 오류 조건을 시그널링하는 방법을 가지고 있지만이 시스템은 예외가 아니라 "결과"입니다. 실패 할 수있는 함수는 일반 값이나 오류가 포함 된 "결과"를 반환합니다. Elms는 강력하게 입력되므로 유형 시스템에 명시 적입니다. 함수가 항상 정수를 반환하면 형식이 Int입니다. 그러나 정수를 반환하거나 실패하면 반환 유형은 Result Error Int입니다. (문자열은 오류 메시지입니다.) 이렇게하면 호출 사이트에서 두 경우를 명시 적으로 처리해야합니다.

다음은 소개예입니다 (약간 단순화).

view : String -> String 
view userInputAge =
  case String.toInt userInputAge of
    Err msg ->
        text "Not a valid number!"

    Ok age ->
        text "OK!"

toInt입력을 구문 분석 할 수없는 경우 함수 가 실패 할 수 있으므로 리턴 유형은 Result String int입니다. 실제 정수 값을 얻으려면 패턴 일치를 통해 "포장 풀기"해야하므로 두 경우 모두 처리해야합니다.

결과와 예외는 기본적으로 동일한 작업을 수행하며 중요한 차이점은 "기본값"입니다. 예외는 기본적으로 프로그램을 버블 링하고 종료하며 처리하려는 경우 명시 적으로 잡아야합니다. 결과는 다른 방법입니다. 기본적으로 처리해야하므로 프로그램을 종료하려면 맨 위로 전달해야합니다. 이 동작이 어떻게보다 강력한 코드를 생성 할 수 있는지 쉽게 알 수 있습니다.


2
@atoth 여기 예가 있습니다. 언어 A가 예외를 허용한다고 상상해보십시오. 그런 다음 기능이 제공됩니다 doSomeStuff(x: Int): Int. 일반적으로을 반환 할 것으로 예상 Int하지만 예외도 throw 할 수 있습니까? 소스 코드를 보지 않으면 알 수 없습니다. 반대로, 유형을 통해 오류를 인코딩하는 언어 B는 다음과 같이 선언 된 동일한 기능을 가질 수 있습니다 doSomeStuff(x: Int): ErrorOrResultOfType<Int>(Elm에서이 유형은 실제로 이름이 지정됨 Result). 첫 번째 경우와 달리 이제는 함수가 실패 할 수 있는지 여부를 즉시 알 수 있으므로 명시 적으로 처리해야합니다.
안드레스 F.

1
@RobertHarvey가 다른 답변에 대한 의견을 암시하는 것처럼 이것은 기본적으로 Java의 검사 예외와 같습니다. 대부분의 예외가 확인 된 초기에 Java로 작업하면서 배운 것은 실제로 발생하는 시점에서 항상 오류에 대한 코드를 작성하지 않으려는 것입니다.
JimmyJames

2
@JimmyJames 예외는 구성되지 않고 무시 될 수 있고 ( "삼킨") 일류 값이 아니기 때문에 확인 된 예외와는 다릅니다. 이것은 Elm이 만든 새로운 일이 아닙니다. ML 또는 Haskell과 같은 언어로 프로그래밍 하는 방식 이며 Java와 다릅니다.
Andres F.

2
@AndresF. this is how you program in languages such as ML or Haskell하스켈에서는 그렇습니다. ML, 아니 Standard ML 및 프로그래밍 언어 연구원의 주요 공헌자 인 Robert Harper 는 예외가 유용하다고 생각합니다 . 오류가 발생하지 않도록 보장 할 수있는 경우 오류 유형이 함수 구성에 방해가 될 수 있습니다. 예외도 다른 성능을 갖습니다. 발생하지 않은 예외에 대해서는 비용을 지불하지 않지만 매번 오류 값을 확인하는 것에 대해서는 비용을 지불하며 예외는 일부 알고리즘에서 역 추적을 표현하는 자연스러운 방법입니다.
Doval

2
@JimmyJames 이제 확인 된 예외와 실제 오류 유형이 표면적으로 만 유사하기를 바랍니다. 확인 된 예외는 정상적으로 결합되지 않고 사용하기가 번거롭고 표현 지향적이지 않으므로 Java와 마찬가지로 간단히 "삼키기"할 수 있습니다. 검사되지 않은 예외는 덜 성가 시므로 Java 이외의 모든 곳에서 표준이지만, 당신을 넘어 뜨릴 가능성이 더 높으며, 함수 선언이 발생하는지 여부를 확인하여 프로그램을 더 어렵게 만들 수는 없습니다. 이해하다.
Andres F.

2

먼저, "삼키기"예외에 대한 예는 일반적으로 끔찍한 실행이며 런타임 예외가없는 것과는 전혀 관련이 없습니다. 당신이 그것에 대해 생각할 때, 당신은 런타임 오류가 있었지만, 그것을 숨기고 그것에 대해 아무것도하지 않기로 선택했습니다. 이로 인해 종종 이해하기 어려운 버그가 발생합니다.

이 질문은 여러 가지 방법으로 해석 될 수 있지만 주석에서 Elm을 언급 했으므로 컨텍스트가 더 명확합니다.

Elm은 무엇보다도 정적으로 유형이 지정된 프로그래밍 언어입니다. 이런 종류의 시스템의 장점 중 하나는 프로그램이 실제로 사용되기 전에 컴파일러가 많은 종류의 오류 (전부는 아님)를 포착한다는 것입니다. 일부 종류의 오류는 예외로 처리되지 않고 유형 (예 : Elm ResultTask) 으로 인코딩 될 수 있습니다 . 이것은 Elm의 디자이너가 의미하는 바입니다. "런타임"대신 컴파일 타임에 많은 오류가 발생하며, 컴파일러는 무시하고 최선을 다하는 대신 오류를 처리하도록합니다. 이것이 왜 이점인지는 분명합니다. 프로그래머가 사용자보다 먼저 문제를 인식하는 것이 좋습니다.

예외를 사용하지 않으면 오류가 덜 놀라운 다른 방식으로 인코딩됩니다. 에서 느릅 나무의 설명서 :

Elm의 보장 중 하나는 실제로 런타임 오류가 표시되지 않는다는 것입니다. NoRedInk는 현재 약 1 년 동안 생산에 Elm을 사용하고 있으며 아직 보유하고 있지 않습니다! Elm의 모든 보증과 마찬가지로 기본적인 언어 디자인 선택이 필요합니다. 이 경우 Elm이 오류를 데이터로 처리한다는 사실이 도움이됩니다. (여기서 데이터를 많이 만드는 것을 보셨습니까?)

Elm 디자이너는 "실시간 예외 없음" 이라고 주장하는 데 약간 과감 하지만 "실제"로 자격이 있습니다. 그 의미는 "자바 스크립트로 코딩 할 때보 다 예기치 않은 오류가 적다는 것"입니다.


내가 잘못 읽었습니까, 아니면 그냥 의미 론적 게임을하고 있습니까? 그들은 "런타임 예외"라는 이름을 금지하지만, 오류 정보를 스택에 전달하는 다른 메커니즘으로 대체합니다. 이것은 예외 구현을 비슷한 개념이나 오류 메시지를 다르게 구현하는 객체로 변경하는 것처럼 들립니다. 그것은 지구를 산산이 부서지지 않습니다. 정적으로 입력 된 언어와 같습니다. COM HRESULT에서 .NET 예외로의 전환을 비교하십시오. 다른 메커니즘이지만, 무엇을 호출하든 여전히 런타임 예외입니다.
Mike는 Monica를 지원합니다.

@Mike 솔직히 말해서 Elm을 자세히 보지 않았습니다. 문서로 판단하면 유형이 Result있으며 Task친숙 Either하고 Future다른 언어 와 매우 유사합니다 . 예외와 달리 이러한 유형의 값을 결합 할 수 있으며 어떤 시점에서 명시 적으로 처리해야합니다. 유효한 값 또는 오류를 나타 냅니까? 나는 마음을 읽지는 않지만 프로그래머의 놀라운 부족은 아마도 Elm 디자이너들이 "런타임 예외 없음"이라는 의미 일 것입니다.
Andres F.

@ 마이크가 지구를 산산조각 내지 않는다는 데 동의합니다. 런타임 예외와의 차이점은 유형에서 명시 적이 지 않다는 것입니다 (즉, 소스 코드를 보지 않고도 코드 조각이 발생할 수 있는지 여부를 알 수 없음). 유형의 인코딩 오류는 매우 명시 적이며 프로그래머가 오류를 간과하지 못하게하여보다 안전한 코드로 이어집니다. 이것은 많은 FP 언어에 의해 수행되며 실제로 새로운 것은 아닙니다.
Andres F.

1
귀하의 의견에 따르면 "정적 유형 검사" 이상이 있다고 생각 합니다. 새로운 "make-or-break"에코 시스템보다 훨씬 덜 제한적인 Typescript를 사용하여 JS에 추가 할 수 있습니다.
atoth December

1
@AndresF .: 기술적으로 말하면 Java 유형 시스템의 최신 기능은 60 년대 후반에 우뚝서는 Parametric Polymorphism입니다. 따라서 "Java가 아님"을 의미 할 때 "현대"라고 말하는 것은 다소 정확합니다.
Jörg W Mittag

0

엘름 주장 :

실제로 런타임 오류 가 없습니다 . null이 없습니다. 정의되지 않은 것은 함수가 아닙니다.

그러나 런타임 예외 에 대해 묻습니다. . 차이가 있습니다.

Elm에서는 아무것도 예기치 않은 결과를 반환하지 않습니다. 런타임 오류를 발생시키는 유효한 프로그램을 Elm에 작성할 수 없습니다. 따라서 예외가 필요하지 않습니다.

따라서 질문은 다음과 같아야합니다.

"런타임 오류 없음"의 이점은 무엇입니까?

런타임 오류가없는 코드를 작성할 수 있으면 프로그램이 중단되지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.