336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

1. 액터 

  - 각자의 특정 작업을 수행, 각자의 Queue 를 갖고있고 dequeue 한 후 작업을 수행 


2. Supervisor 

  - 액터들의 상태를 감시하는 액터. 계층상 상위에 있는 액터는 슈퍼바이저가 된다. 


3. 테스트 

  - SilentActor -> 메세지를 받기만 하고 외부로 아예 안내보내는 액터, underlyingActor 으로 접근해 확인하거나 , expectMsg 함수호출해서 확인한다. 

  - SendingActor -> 받은 메시지를 다른 액터에 보낼 때 사용하는 방법, expectMsgPF 사용한다. 

  - SideEffectActor -> Side Effect 가 있는경우 사용 (가령 함수 내에서 로그 메시지를 출력한다거나..), 리스너를 사용할 수 있다. 


4. 내고장성 

  - 액터에 여러가지 문제가 생겼을 때, 어떻게 작동할 지 설정할 수 있다. 

  - 재시작, 종료, 진행, 위로전달 전략을 설정할 수 있으며, OneForOneStrategy 는 한개의 액터에만, AllForOneStrategy 에는 자기 휘하의 모든 액터를 종료한다.

블로그 이미지

캡틴토마스

그저 걷고 있는거지...

,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

코세라 스칼라 강좌 4.4 Variance 중.


Typing Rules for Functions 


If A2 <: A1 and B1 <: B2 then

    (A1 => B1) <: (A2 => B2)


뭔 소린지 전혀 몰라서 수시간을 고민해보다가 stackoverflow 보고 이해 


원문 주소는 https://stackoverflow.com/questions/41098105/subtyping-between-function-types 여기


내가 이해한 방식으로 정리한다. 


1. subtype 이다? 

  - subtype 이란건 'a 는 b 이다' 의 is - a 관계가 성립하는거.


2. (A1 => B1) <: (A2 => B2) 임을 증명하려면 

  - '(A1 => B1) 함수(이하 F1)는  (A2 => B2) 함수(이하 F2)다' 가 증명되면 됨.

    

'F1 는 F2 이다 ' 가 true 가 되려면 필요한 규칙은 


F2(a) 를 F1(a) 로 바꿔도 에러 없이 정확한 값이 나와야 한다는 소린데(리스코프의 치환 원칙), 이소리는 즉


1. F2 의 모든 input 이 F1 에서 에러가 없어야 함

2. F1 의 모든 output 이 F2 의 output 이 되어야 함


저 규칙에 맞나 보면 

1. F2 의 모든 input 이 F1 에서 에러가 없어야 함

  -> 만족한다. A2(F2 의 input) <: A1 (F1 의 input) 이기 때문에, A2 는 A1 의 부분집합이고, A2의 모든 원소는 A1 이다.

2. F1 의 모든 output 이 F2 의 output 이 되어야 함 

  -> 만족한다. B1(F1 의 output) <: B2(F2 의 output) 이기 때문에 B1 은 B2 의 부분집합이고, B1 의 모든 원소는 B2 이다.


이 때문에 'F1 은 F2 이다' 는 참이고, F1 은 F2 의 subtype 이다.


맞나 이거? 


    

블로그 이미지

캡틴토마스

그저 걷고 있는거지...

,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

다시 한번 복습 


A <: B 이고 C[A] <: C[B] 인걸 Covariant

A <: B 이고 C[A] >: C[B] 인걸 Contravariant


Contravariant 는 주로 함수를 파라미터로 넘길 때 일어난다고 이야기 함.


이걸 그림으로 다시 생각 해보자, 일단 아래와 같은 클래스가 있다. 


class Animal {}

class Bird extends Animal {}
class Chicken extends Bird {}
class Duck extends Bird {}


이걸 밴 다이어 그램으로 그려보면 아래와 같이 된다.



이제 함수를 선언해보자. 

object HelloWorld {
  type FunctorType[A] = (A => String) //함수타입 한개 선언

  val animalFunc : FunctorType[Animal] = { a => "Animal" }
  val birdFunc : FunctorType[Bird] = { a => "Bird" }
  val chickenFunc : FunctorType[Chicken] = { a => "Chicken"}
  val duckFunc : FunctorType[Duck] = { a => "Duck"}
}


위의 밴 다이어 그램을 보면서 다시 정리해보자 


Animal >: Bird 이면, FunctorType[Animal] 과 FunctorType[Bird] 의 관계는?


FunctorType[Animal] 을 풀어보면 Animal => String 이고, 

FunctorType[Bird] 을 풀어보면 Bird => String 인데, 

위의 벤 다이어그램에서 보면, Bird 는 Animal 의 부분집합 이니까, 모든 Bird 는 Animal 이다. 따라서, 


FunctorType[Bird]  는 Bird => String 도 될 수 있고, Animal => String 도 될 수 있다. 


때문에, FunctorType[Animal] <: FunctorType[Bird] 이 되고, 


FunctorType 은 contravariant 가 된다... 

블로그 이미지

캡틴토마스

그저 걷고 있는거지...

,
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

코세라 강의 보는 중에 


A <: B 이고 C[A] <: C[B] 인걸 Covariant 

A <: B 이고 C[A] >: C[B] 인걸 Contravariant 라고 한다.


Covariant 는 이미 알고있는 거고,

Contravariant 가 무슨소린지 도저히 모르겠어서, 구글링... 하다가 하나 발견... 4시간에 걸쳐 겨우 이해하고, 정리글을 남긴다. 


아래와 같이 클래스들이 있고, 함수 타입 한개를 선언한다.


그리고 몇가지 함수들을 선언한다.

class Animal {}

class Bird extends Animal {}
class Chicken extends Bird {}
class Duck extends Bird {}

object HelloWorld {
  type FunctorType[A] = (A => String) //함수타입 한개 선언

  val birdFunc : FunctorType[Bird] = { a => "Bird" }
  val animalFunc : FunctorType[Animal] = { a => "Animal" }
  val chickenFunc : FunctorType[Chicken] = { a => "Chicken"}
  val duckFunc : FunctorType[Duck] = { a => "Duck"}
}


이경우 클래스 관계는 


Animal <- Bird <- Chicken

Animal <- Bird <- Duck


이런 형식으로 된다. 다들 알다시피 


하지만 함수 아래와 같이 함수가 파라미터로 전달 될 때도, 다형성이 그대로 유지될까?

def test_contravariant(a: FunctorType[Bird]): Unit = {
    val ret1 = a(new Chicken);
    println(ret1)
    val ret2 = a(new Bird);
    println(ret2)
}



test_contravariant(animalFunc) 
test_contravariant(birdFunc) 
test_contravariant(chickenFunc)  //컴파일에러
test_contravariant(duckFunc)     //컴파일에러


맨 처음 봤을때는 의외의 결과였다. 그 전에 C++ JAVA 에서 써왔던 것과 반대로 나타났기 때문이다. 


내친김에 covariant 도 테스트 해본다. 


def test_covariant(a: Bird) : Unit = {
    println(a.toString)
}


예상한대로 결과가 나온다.


test_covariant(new Animal) //컴파일에러 test_covariant(new Bird) test_covariant(new Chicken) test_covariant(new Duck)


클래스 상속과 관련해서 생각을 다시 정리해본다. 


"

슈퍼클래스의 참조로 하위클래스의 객체를 생성하거나 파라미터로 전달해도 문제가 없는 이유는, 

하위클래스라고 해서 있던 public 함수/변수가 없어지거나 하지 않기 때문이다.

"


그리고 contravariant 예제를 다시한번 본다.


val animalFunc : FunctorType[Animal] = { a => "Animal" }
val birdFunc : FunctorType[Bird] = { a => "Bird" }
val chickenFunc : FunctorType[Chicken] = { a => "Chicken"}
val duckFunc : FunctorType[Duck] = { a => "Duck"}

def test_contravariant(a: FunctorType[Bird]): Unit = {
    val ret1 = a(new Chicken);
    println(ret1)
    val ret2 = a(new Bird);
    println(ret2)
}


test_contravariant 의 파라미터로 chickenFunc 나 duckFunc 를 넘기게 되면, 


Chicken 클래스에는 있는 public 함수지만, Bird 클래스에는 없는 public 함수가 있을 수 있다.

Duck 클래스에는 있는 public 함수지만, Bird 클래스에는 없는 public 함수가 있을 수 있다.


하지만 animalFunc 을 test_contravariant 로 넘기게 되면,

Animal 클래스에 있는 모든 public 함수는 Bird 클래스에 있다.


이때문에 FunctorType[슈퍼클래스] 에 대해서는 다형성이 일어나지만,

FunctorType[하위클래스] 에 대해서는 다형성이 일어나지 않는다. 

즉, 상속 관계가 역전이 된다. 


이런 경우가 A <: B 이고 C[A] >: C[B] 이며, 

이를 contravariant 라고 부른다. 




블로그 이미지

캡틴토마스

그저 걷고 있는거지...

,