- [Expression Problem] 표현 문제란?
- [Expression Problem] Open classes , Protocol , 그리고 확장 메서드 -> 지금 여기
- [Expression Problem] Type classes
- [Expression Problem] Mutiple Dispatch (Feat Visitor / Multi Method)
- [Expression Problem] 객체 대수 (Object algebras) 와 Tagless Final
Open Classes , 일명 Monkey patching, 은 확장에 열려있는 Class 를 의미한다.
Ruby 에서 주로 땜빵할때 많이 사용하는 기술이다. OOP 식 접근법에서 발전?된 솔루션으로 볼 수 있다.
많이 다르지만, 대충보면 비슷한 extension method 나 implicit 까지 해당 포스트에서 설명
Open Class
일반적으로, 한번 정의된 Class 를 확장하려면, Class code 자체를 수정해야한다.
나만 쓰는 내 코드라면 상관없지만, 이곳저곳 다 사용되거나, 아니면 에초에 내가짠 코드가 아닌경우는 class 를 까서 수정하기 쉽지않다.
Ruby 와 같은 일부 언어는, 정의가 이미 끝난 클래스를 외부에서 수정할수있다.
필자는 Ruby 를 잘 모르기 때문에 간단하게 설명하면,
- 이미 있는 클래스를
- 열어서 (Open)
- 내코드를 쓰까 섞고 다시 닫는다
이다.
#이전에 Healer 는 미리 정의 되어 있음
class Healer
def defence
# blah blah ~~
end
end
진짜 돌아가는 코드인지는 모름
Ruby 에서는 위와 같이 이미 있는 Healer 라는 class 에 외부에서 defence 라는 함수를 추가 할수도, 기존 함수를 수정할수도 있다.
딱봐도 어마어마한 자유도를 가져다 줄 것 같은 강력한 기능이다.
다만, 너무나도 강력하기 때문에 위험하다, 그리고 이를 "몽키 패치" 라고 부르는 이유가 있다.
namespace 를 넘어 당기면서 험난한 길을 거쳐온 Class 내에서의 함수의 충돌이 있다면?
분명 defence 만 했는데 채력이 마구 찬다면?
이거 어떤 새끼가 바꾼거야?
바로 의도치 않은 동작을 할 가능성이 매우 높기 때문이다.
알아도 쓸데없는 잡 지식) 문제 발생시 게릴라(임시로)로 쏙 고쳐서 게릴라 패치가 고릴라 패치가 되고 몽키패칭이 된것
일반적으로, 정말로 잘 설계되지 않는한, 몽키패칭은 지양하는것이 좋다.
Protocol
프로토콜은 clojure 진영에서 주로 사용하는 2가지 표현문제의 해결법중에 하나이며, Clojure 판 Open class 라고 보면 된다. 다만, protocol 은 함수의 범위가 namespace 로 규제되므로, 실 사용시에는 크게 다르다.
원래 짜던 함수에 다형성이 지원 될 뿐이다. 위 링크에서가져온 예시를 보자
# ruby 에서는 실제로 class 가 확장된다 (open 되어 수정된다)
"string".methods.count # => 170
require 'rails'
"string".methods.count # => 225
(def foo bar)
(in-ns 'monkeypatching)
(defprotocol Monkey
(patch [this]))
;; 프로토콜로 일반 Core 확장
(extend-protocol Monkey
nil
(patch [this] "Check out this cool nil!")
String
(patch [this] "I'm an awesome string!"))
(patch nil) ; => "Check out this cool nil!"
(patch "this is some string") ; => "I'm an awesome string!"
;; Name space 변경
(in-ns 'workin-hard)
(patch nil) ; => CompilerException
;; 프로토콜로 확장된 함수는 사용가능함 (namespace 접근)
(monkeypatching/patch "a string") ; => "I'm an awesome string!"
즉.. 원래 (defn patch-string []) / (defn patch-nill [] ) 로 정의 될 함수가 다형성이 지원된 (patch) 로 사용할 뿐이다.
clojure 에 관심있는 사람을 별로 없을테니까 여기까지...
Extension Method (Extension Function)
확장 메서드 (C# 에서는 확장메서드, 코틀린에서는 확장 함수) 는 얼핏보면 오픈클래스와 아주 유사하다. 이미 정의된 클래스를 가져다가, 새로운 메서드 를 추가할 수 있으며, 추가된 기능을 가진 namespace 를 가져와서 확장한다.
다만 그렇게 보인다는거지 실제는 구현은 아주 다르다, 개인적인 의견으로는 Protocol 에 더 가깝고, 사실 문법적 설탕에 불과하다.
import oop.Healer
//기존 Healer 에는 defence 함수가 없음
fun Healer.defence() {
println("Healer defence")
}
fun main(args: Array<String>) {
val h = Healer()
h.defence() // Healer defence
}
엥 이거 완전 Open class 아니냐?
Open class 가 아닌 이유는 확장메서드는 눈속임이기 때문이다.
위 에러를 살펴보자, JVM 에서 동일한 시그니쳐라고 에러를 뿜뿜한다.
즉 확장 메서드는, 그냥 첫번째 파라미터로 확장한 클래스를 받는 함수를 "쓰기 쉽게" 원래 있던 함수인거처럼 보여주는것 뿐이다. 쓰기쉽게, 첫번째 파라미터의 명칭이 "this" 인것 뿐이다.
그렇기 때문에 당연하게, private 변수를 사용하지 못한다.
확장메서드는 유틸성 함수(C# 의 LINQ 같은..)를 추가하는데 아주 유용하다.
implicit
암시적 (implicit) 은 scala 에 있는 키워드로, scala 진영에서 가장 좋아하는 (그러면서도 가장 주의하는) 기능이다.
암시적 클래스와 암시적 파라미터가 있는데, 구분은 넘어가고..
대충 말하자면, 원하는 클래스로 컴파일러가 뚝딱 바꿔준다는 개념이다.
이로써 가능한 트릭들이 어마어마한데, implicit 를 확장 메서드를 구현하는데 활용도 할수있다. (다양한 기술중 하나)
(작성 하면서 알았는데, scala3 에서는 implicit 키워드 없이도 확장메서드가 된다.)
(Scala 의 implicit 는 정확하지 않음 )
'프로그래밍 기술 노트 > Functional Study' 카테고리의 다른 글
[Expression Problem] Mutiple Dispatch (Feat Visitor / Multi Method) (0) | 2022.04.27 |
---|---|
[Expression Problem] Type classes (0) | 2022.04.26 |
[Expression Problem] 표현 문제란? (0) | 2022.04.26 |
[Optics/Lens] 내 멋대로 Optics 이해하기 Feat) arrow-kt (2) | 2022.04.20 |
[liftIO] 함수형 컨퍼런스 liftIO 후기 (0) | 2021.11.03 |