답변:
렌즈를 제공하는 것으로 알고있는 라이브러리가 4 개 이상 있습니다.
렌즈의 개념은 렌즈가 동형 인 것을 제공한다는 것입니다
data Lens a b = Lens (a -> b) (b -> a -> a)
게터와 세터 두 가지 기능 제공
get (Lens g _) = g
put (Lens _ s) = s
세 가지 법률이 적용됩니다.
첫째, 무언가를 넣으면 다시 얻을 수 있습니다.
get l (put l b a) = b
두 번째로 가져오고 설정해도 대답이 바뀌지 않습니다.
put l (get l a) a = a
셋째, 두 번 퍼팅하는 것은 한 번 퍼팅하는 것과 동일합니다.
put l b1 (put l b2 a) = put l b1 a
타입 시스템으로는 이러한 법률을 확인하기에 충분하지 않으므로 어떤 렌즈 구현을 사용하든 관계없이 직접 확인해야합니다.
이 라이브러리들 중 다수는 또한 맨 위에 추가 콤비 네이터를 제공하며, 일반적으로 간단한 레코드 유형의 필드에 대한 렌즈를 자동으로 생성하는 일종의 템플릿 haskell 기계입니다.
이를 염두에두고 다양한 구현으로 전환 할 수 있습니다.
구현
fclabels
fclabels 는 아마도 렌즈 라이브러리에 대해 가장 쉽게 추론 a :-> b
될 수 있습니다. 위의 유형으로 직접 변환 될 수 있기 때문 입니다. 렌즈를 구성 할 수있어 유용한 Category 인스턴스를 제공합니다 (:->)
. 또한 Point
여기에 사용 된 렌즈의 개념과 동형을 다루기위한 배관의 개념을 일반화 하는 무법 유형을 제공합니다 .
의 채택에 대한 하나 개의 장애는 fclabels
패키지가 아닌 하스켈 (98), 그리고 그것은 또한 (상당히 비 논란이)가 필요하므로 기본 패키지는, 템플릿 - 하스켈 배관이 포함되어 있다는 것입니다 TypeOperators
확장.
데이터 접근 자
[편집 : data-accessor
이 표현을 더 이상 사용하지 않지만와 유사한 형식으로 이동했습니다 data-lens
. 그래도이 주석을 유지하고 있습니다.]
데이터 접근은 보다 약간 더 인기 fclabels
가 있기 때문에 부분적으로 이다 하스켈 98 그러나, 내부 표현의 그 선택은 내 입에 조금을 던질 수 있습니다.
T
렌즈를 나타내는 데 사용 되는 유형 은 내부적으로
newtype T r a = Cons { decons :: a -> r -> (a, r) }
결과적으로 get
렌즈의 가치를 위해서는 'a'인수에 대해 정의되지 않은 값을 제출해야합니다! 이것은 엄청나게 추악하고 임시 구현으로 나를 때립니다.
즉, Henning은 별도의 ' data-accessor-template '패키지 에서 접근자를 자동으로 생성하기 위해 템플릿-하스켈 배관을 포함했습니다 .
이미 사용하고 있으며 Haskell 98이며 중요한 Category
인스턴스를 제공하는 상당히 큰 패키지 세트의 이점을 가지고 있으므로 소시지 제작 방법에주의를 기울이지 않으면이 패키지는 실제로 합리적인 선택입니다. .
렌즈
다음으로, 렌즈 패키지는 렌즈가 이러한 모나드 동종 으로서 렌즈를 직접 정의함으로써 두 개의 상태 모나드 사이에 상태 모나드 동질성을 제공 할 수 있음을 관찰 한다.
실제로 렌즈 유형을 제공하는 데 방해가된다면 다음과 같은 순위 2 유형이 있습니다.
newtype Lens s t = Lens (forall a. State t a -> State s a)
결과적으로 필자는 Haskell 98에서 불필요하게 당신을 잡아 당기고 (렌즈에 추상으로 유형을 제공하려는 경우) 렌즈에 대한 Category
인스턴스를 박탈하기 때문에이 접근법을 좋아하지 않습니다. 로 구성하십시오 .
. 구현에는 다중 매개 변수 유형 클래스도 필요합니다.
여기에 언급 된 다른 모든 렌즈 라이브러리는 일부 결합기를 제공하거나 이와 동일한 상태 초점 효과를 제공하는 데 사용할 수 있으므로 렌즈를 이런 방식으로 직접 인코딩해도 아무런 효과가 없습니다.
게다가, 처음에 언급 된 부 조건은 실제로이 형태로 좋은 표현을 가지고 있지 않습니다. 'fclabels'와 마찬가지로 이것은 기본 패키지에서 직접 레코드 유형의 렌즈를 자동으로 생성하는 템플릿 하스켈 방법을 제공합니다.
Category
메인 패키지 의 인스턴스 부족 , 바로크 인코딩 및 템플릿-하스켈 요구 사항으로 인해 이것은 내가 가장 선호하는 구현입니다.
데이터 렌즈
[편집 : 1.8.0 현재, 이들은 comonad-transformers 패키지에서 data-lens로 옮겨졌습니다.]
내 data-lens
패키지는 상점 코모 나의 관점에서 렌즈를 제공합니다 .
newtype Lens a b = Lens (a -> Store b a)
어디
data Store b a = Store (b -> a) b
확장 된 것은
newtype Lens a b = Lens (a -> (b, b -> a))
이것을 getter와 setter로부터 요소를 검색 한 결과로 구성된 쌍과 새로운 값을 다시 넣는 setter를 반환하기 위해 공통 인수를 제외하는 것으로 볼 수 있습니다. 이는 'setter'의 계산상의 이점을 제공합니다. 여기에서 가치를 얻는 데 사용되는 일부 작업을 재활용하여 fclabels
특히 접근자가 연결될 때 정의 보다 더 효율적인 '수정'작업을 수행 할 수 있습니다.
이 반응의 시작 부분에 언급 된 3 가지 법칙을 만족하는 '렌즈'값의 서브 세트가 정확히 포장 기능이 상점 코모 나의 '코 모나드 대수'인 렌즈이기 때문에이 표현에 대한 이론적 근거도 훌륭합니다. . 이렇게하면 렌즈에 대한 3 가지 털이 많은 법칙 l
이 2 개의 포인트 없는 렌즈 로 변형됩니다 .
extract . l = id
duplicate . l = fmap l . l
이 방법은 첫째주의와 러셀 오코너의 설명 된 Functor
이다 Lens
로 Applicative
하는 것입니다 Biplate
: 소개 다판 되었다 프리 프레스를 기반에 대해 블로그에 제레미 기븐스에 의해.
또한 렌즈를 엄격히 사용하기위한 다수의 결합기와와 같은 컨테이너를위한 일부 스톡 렌즈를 포함 Data.Map
합니다.
따라서 ( 패키지 와 달리) data-lens
형태 의 렌즈 는 Haskell 98 (과 달리 / 와 같고)이며 (후단과 달리 ) 약간 더 효율적인 구현을 제공하며 외부로 나가려는 사람들을 위해 MonadState와 함께 작동하는 기능을 제공합니다. of Haskell 98 및 템플릿 Haskell 기계를 이제 사용할 수 있습니다 .Category
lenses
fclabels
lenses
data-accessor
data-lens-fd
data-lens-template
2012 년 6 월 28 일 업데이트 : 기타 렌즈 구현 전략
동형 렌즈
고려해야 할 다른 두 가지 렌즈 인코딩이 있습니다. 첫 번째는 렌즈를 시야의 가치와 다른 모든 것의 가치로 나누는 방법으로 렌즈를 보는 좋은 이론적 방법을 제공합니다.
동형에 대한 유형이 주어짐
data Iso a b = Iso { hither :: a -> b, yon :: b -> a }
유효한 회원이 만족 hither . yon = id
하고yon . hither = id
우리는 렌즈를 다음과 같이 나타낼 수 있습니다.
data Lens a b = forall c. Lens (Iso a (b,c))
이것들은 주로 렌즈의 의미를 생각하는 방법으로 유용하며 다른 렌즈를 설명하는 추론 도구로 사용할 수 있습니다.
반 라 호벤 렌즈
우리는 렌즈 를 사용하여 인스턴스 없이도 (.)
및 로 구성 할 수 있도록 렌즈를 모델링 할 수 있습니다.id
Category
type Lens a b = forall f. Functor f => (b -> f b) -> a -> f a
렌즈의 유형으로.
렌즈를 정의하는 것은 다음과 같이 쉽습니다.
_2 f (a,b) = (,) a <$> f b
함수 구성이 렌즈 구성인지 직접 확인할 수 있습니다.
나는 최근 에이 서명을 일반화하여 반 Laarhoven 렌즈 를 더 일반화 하여 필드 유형을 변경할 수있는 렌즈 제품군을 얻는 방법에 대해 글을 썼습니다
type LensFamily a b c d = forall f. Functor f => (c -> f d) -> a -> f b
렌즈에 대해 이야기하는 가장 좋은 방법은 순위 2 다형성을 사용하는 것이 유감스러운 결과이지만 렌즈를 정의 할 때 해당 서명을 직접 사용할 필요는 없습니다.
Lens
에 대한 위의 정의 나는 _2
사실이다 LensFamily
.
_2 :: Functor f => (a -> f b) -> (c,a) -> f (c, b)
렌즈, 렌즈 제품군 및 게터, 세터, 접기 및 순회를 포함한 기타 일반화를 포함하는 라이브러리를 작성했습니다. lens
패키지 로 해커에서 사용할 수 있습니다 .
다시 말하지만,이 접근 방식의 큰 장점은 라이브러리 관리자가 Functor f => (b -> f b) -> a -> f a
특정 유형 'a'및 'b'에 대해 type 함수를 제공함으로써 렌즈 라이브러리 종속성을 발생시키지 않고 실제로 라이브러리에서이 스타일의 렌즈를 만들 수 있다는 것 입니다. 이를 통해 채택 비용이 크게 절감됩니다.
실제로 새 렌즈를 정의하기 위해 패키지를 사용할 필요가 없기 때문에 라이브러리 Haskell 98을 유지하는 것에 대한 이전의 걱정을 덜어줍니다.
:->
data-accessor
, 그것을 Henning에게 넘겨주고주의를 기울이지 않았습니다. a -> r -> (a,r)
표현은 나를 불편하게하고, 내 원래의 구현은 당신의 같았다 Lens
유형입니다. Heeennnninngg !!
lens
패키지는 가장 풍부한 기능과 문서를 제공하므로 복잡성과 종속성에 신경 쓰지 않으면 바로 갈 수 있습니다.