스칼라에서

10685 단어

“Either is both, and Both is neither.” - Plutarch


Either는 언어를 처음 배울 때 간과하기 쉬운 scala의 또 다른 모나드 유형입니다. 그러나 Scala와 Scala(및 FP) 관용구에 대한 많은 근거가 결합되어 있으므로 익숙해지는 데 시간을 할애할 가치가 있다고 생각합니다.

모나드 유형으로서 Either의 목적은 계산을 선언적이고 기능적인 스타일로 구성할 수 있도록 하는 것입니다.

그러나 둘 중 하나는 무엇입니까?


Either는 두 가지 유형 중 하나일 수 있는 값의 래퍼 유형입니다. 두 숫자를 나누는 방법을 작성하고 싶다고 가정합니다. 나누기가 성공하면 double을 반환하고 싶습니다. 실패하면 메시지와 함께 문자열을 반환하고 싶습니다.

Java에서 우리는 실제로 이것을 할 수 없습니다.

// can't do this
public *what?* method(int num, int denom) {
    if (denom == 0) {
        return "cannot divide by 0";
    } else {
        return (double)denom / num;
    }
}


우리는 예외를 던질 수 있고, 호출 코드가 그것을 포착하고 무엇을 할지 결정하게 할 수 있습니다.

public double method(int num, int denom) throws IllegalArgumentException {
    if (denom == 0) {
        throw new IllegalArgumentException("cannot divide by 0");
    } else {
        return (double)denom / num;
    }
}

...

// calling code
try {
    double res = method(1, 0);
    doSometh(res);
} catch (IllegalArgumentException iae) {
    doSomethElse(iae.getMessage());
}


이 예제는 사소하고 원하는 작업이 아니지만 Java 구현 imho에는 여러 가지 문제가 있습니다.
  • 장황합니다(제 생각에는 Java입니다!)
  • 의미론적으로 문자열 경로는 예외 조건이어야 합니다. 이러한 종류의 코드를 사용하려는 대상일 가능성이 높으므로 이는 대부분 괜찮습니다.
  • 메서드 pov에서는 IllegalArgumentException이 올바르지만 호출 코드에서는 별 의미가 없습니다. 래핑하는 메시지는 잘못된 인수와 명백하게 관련이 있는 것이 아니라 메서드에서 반환된 다른 값일 뿐입니다.
  • 부작용은 예외입니다
  • .

    둘 중 하나를 입력



    앞에서 언급했듯이 Either는 두 가지 일반 유형 중 하나를 래핑합니다. 위의 장난감 예제에서는 Either[String, Double]를 사용할 수 있으며 코드는 다음과 같습니다.

    def method(num: Int, denom: Int): Either[String, Double] = {
        if (denom == 0) Left("cannot divide by 0")
        else Right(denom.asDouble / num)
    }
    
    ...
    
    // calling code
    method(1, 0) match {
        case Left(s) => // do something stringy with s
        case Right(d) => // do something doubley with d
    }
    


    이것이 Java 코드의 문제를 훌륭하게 해결한다고 생각합니다. 간결하고 명확합니다. 두 값 유형은 의미상 예외가 아닌 다른 경로일 뿐입니다. 사실 String 값을 래핑하기 위해 예외를 사용할 필요가 전혀 없습니다. 그리고 부작용이 없습니다.

    더 관련된 예



    위의 내용은 Either 의 일부 측면을 캡처하지만 실제 성능은 많은 계산을 실행할 때 발생하며 그 중 하나는 실패할 수 있습니다. 특히 Java 코드가 있는 경우 예외를 발생시키는 상호 운용이 가능해야 합니다. 예외를 던지는 Java 모델은 순수하고 멱등적이며 부작용이 없는 함수의 세계에서는 제대로 작동하지 않습니다. 이는 여러 VM 또는 시스템에 걸쳐 있을 수 있는 Akka Actor 시스템에 들어가면 특히 문제가 됩니다.
    Either가 기능적인 방식으로 Java 코드로 작업하는 데 어떻게 도움이 되는지 살펴보겠습니다.

    예외를 발생시키는 것을 좋아하는 Java 메서드가 있다고 가정합니다.

    public int unpredictableJavaMethod(int n) {
        if (n % 2 == 0) {
            return n;
        } else {
            throw new RuntimeException("failed");
        }
    }
    


    그리고 이 코드를 실행하고 결과를 평가하는 데 필요한 일련의 값이 있다고 가정합니다.

    val results = (1 to 10)
        .map(unpredictableJavaMethod)
    


    불행하게도 이것은 값 2에 대한 런타임 예외로 인해 실패할 것이며 나머지 결과에 도달하지 못할 것입니다. 오히려 다행스럽게도 계산을 다음과 같이 래핑할 수 있습니다.

    def throwableToLeft[T](block: => T): Either[java.lang.Throwable, T] =
        try {
            Right(block)
        } catch {
            case ex => Left(ex)
        }
    


    이제 우리는 자바 호출을 래핑하고 다음과 같은 구성 가능한 작업으로 끝낼 수 있습니다.

    val results = (1 to 10)
        .map(n => throwableToLeft(js.unpredictableJavaMethod(n)))
    
    results.map {
        case Left(ex) => // do something with exception
        case Right(value) => value
    }
    


    맵 대신 결과를 단일 값으로 줄여야 하는 경우 결과를 접을 수 있습니다. 또는 해당 구문이 더 쉬운 경우 이해를 위해 필터링하거나 Scala를 사용하십시오.

    합산



    따라서 Each는 두 가지 유형을 하나로 결합하는 방법이지만 진정한 힘은 안전하고 기능적인 방식으로 Java 코드를 Scala 프로젝트에 통합하는 것을 지원하는 방식에 있습니다.

    좋은 웹페이지 즐겨찾기