렌즈, fclabels, 데이터 접근 자-구조 접근 및 돌연변이를위한 라이브러리가 더 좋습니다


173

레코드 필드에 액세스하고 조작하기위한 인기있는 라이브러리가 3 개 이상 있습니다. 내가 아는 것은 data-accessor, fclabels 및 lens입니다.

개인적으로 나는 데이터 접근 자로 시작하여 지금 사용하고 있습니다. 그러나 최근에 haskell-cafe에서 fclabels가 우수하다는 의견이있었습니다.

따라서 저는이 세 개의 라이브러리를 비교하는 데 관심이 있습니다.


3
오늘날 lens패키지는 가장 풍부한 기능과 문서를 제공하므로 복잡성과 종속성에 신경 쓰지 않으면 바로 갈 수 있습니다.
모듈 식

답변:


200

렌즈를 제공하는 것으로 알고있는 라이브러리가 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이다 LensApplicative하는 것입니다 Biplate: 소개 다판 되었다 프리 프레스를 기반에 대해 블로그에 제레미 기븐스에 의해.

또한 렌즈를 엄격히 사용하기위한 다수의 결합기와와 같은 컨테이너를위한 일부 스톡 렌즈를 포함 Data.Map합니다.

따라서 ( 패키지 와 달리) data-lens형태 의 렌즈 는 Haskell 98 (과 달리 / 와 같고)이며 (후단과 달리 ) 약간 더 효율적인 구현을 제공하며 외부로 나가려는 사람들을 위해 MonadState와 함께 작동하는 기능을 제공합니다. of Haskell 98 및 템플릿 Haskell 기계를 이제 사용할 수 있습니다 .Categorylensesfclabelslensesdata-accessordata-lens-fddata-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))

이것들은 주로 렌즈의 의미를 생각하는 방법으로 유용하며 다른 렌즈를 설명하는 추론 도구로 사용할 수 있습니다.

반 라 호벤 렌즈

우리는 렌즈 를 사용하여 인스턴스 없이도 (.)및 로 구성 할 수 있도록 렌즈를 모델링 할 수 있습니다.idCategory

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을 유지하는 것에 대한 이전의 걱정을 덜어줍니다.


28
나는 낙관적 인 접근 방식으로 fclabels를 좋아합니다:->
Tener


10
Haskell 1998 호환이 중요합니까? 컴파일러 개발이 쉬워지기 때문에? 그리고 대신 Haskell 2010에 대한 이야기로 전환해서는 안됩니까?
yairchu

55
아뇨! 나는의 원저자 였고 data-accessor, 그것을 Henning에게 넘겨주고주의를 기울이지 않았습니다. a -> r -> (a,r)표현은 나를 불편하게하고, 내 원래의 구현은 당신의 같았다 Lens유형입니다. Heeennnninngg !!
luqui

5
Yairchu : 대부분 라이브러리에서 ghc 이외의 컴파일러로 작업 할 수 있습니다. 다른 사람 템플릿 Haskell을 가지고 있지 않습니다 . 2010 년에는 여기에 관련된 내용이 없습니다.
Edward KMETT
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.