89호. PHP 7.4 향상된 타입 가변성 free

2019-12-06

날씨가 상당히 추워졌네요. 오늘은 PHP 7.4에서 향상된 타입 가변성에 대해 알아보겠습니다.


객체지향 프로그래밍의 SOLID 원칙 중 L인 리스코프 치환 원칙은 다음과 같이 이야기 합니다.



만약 S가 T의 서브타입이면, T 객체는 S 객체로 대체될 수 있어야 한다.



하지만 PHP 7.3까지는 파라미터 타입과 리턴 타입이 거의 고정적이었습니다. 이는 슈퍼타입 매서드의 파라미터 타입이나 리턴 타입이 T 면 서브타입 매서드의 파라미터 타입과 리턴 타입도 T 여야 했다는 뜻입니다. 따라서 다음과 같은 코드는 타입 안정성이 보장됨에도 불구하고 허용되지 않았습니다.


interface X {
function m(Y $z): X;
}
interface Y extends X {
// not permitted but type-safe
function m(X $z): Y;
}

언어 차원에서 리스코프 치환 원칙을 지원하지 못했다고 볼 수 있는데요, PHP 7.4에서 이 점이 보완되었습니다.


공변 리턴, 반공변 파라미터 (Covariant Returns and Contravariant Parameters)


문송합니다만 공변, 반공변이라는 단어를 처음 봐서요 ㅠ 공변성에 대해서는 다른 글(예, 공변성과 반공변성은 무엇인가?

)을 따로 참고해주시기 바랍니다.


용어야 어쨌든 의미로 살펴보면 PHP 7.4부터는



  • 파라미터 타입은 슈퍼 타입으로 대체할 수 있고

  • 리턴 타입은 서브 타입으로 대체할 수 있습니다.


코드로 보시면 추가적인 설명 없이도 이해가 될 것 같습니다.


다음은 What's new in PHP 7.4에 실린 예제입니다.


공변 리턴 예제


class ParentType {}
class ChildType extends ParentType {}

class A
{
public function covariantReturnTypes(): ParentType
{ /* … */ }
}

class B extends A
{
public function covariantReturnTypes(): ChildType
{ /* … */ }
}

반공변 파라미터 예제


class A
{
public function contraVariantArguments(ChildType $type)
{ /* … */ }
}

class B extends A
{
public function contraVariantArguments(ParentType $type)
{ /* … */ }
}

RFC에는 인터페이스가 예제로 나왔습니다.


공변 리턴 예제


interface Factory {
function make(): object;
}

class UserFactory implements Factory {
function make(): User;
}

반공변 파라미터 예제


interface Concatable {
function concat(Iterator $input);
}

class Collection implements Concatable {
// Iterator 뿐만 아니라 모든 이터러블을 파라미터로 받을 수 있습니다.
function concat(iterable $input) {/* . . . */}
}

마치며


그간 타입 시스템에 대해 크게 고민 안 하고 있었는데, 이번 기회에 더 알아보는 계기가 되었던 것 같습니다. 리스코프 치환 원칙 제대로 한 번 적용해 봅시다!


1일 1식 라라벨 89호

2019년 12월 6일


이현석

바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서를 쓰고, 클린 아키텍처 인 PHP를 번역했습니다. 2020년에 출간될 Laravel Up & Running 2nd Edition을 번역하고 있습니다.