Programming

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

procodes 2020. 5. 25. 21:20
반응형

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


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

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

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


렌즈를 제공하는 것으로 알고있는 라이브러리가 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

타입 시스템으로는 이러한 법률을 확인하기에 충분하지 않으므로 어떤 렌즈 구현을 사용하든 관계없이 직접 확인해야합니다.

이 라이브러리들 중 다수는 또한 맨 위에 추가 콤비 네이터를 제공하며 일반적으로 간단한 레코드 유형의 필드에 대한 렌즈를 자동으로 생성하기위한 일종의 템플릿 하스켈 기계입니다.

이를 염두에두고 다양한 구현으로 전환 할 수 있습니다.

구현

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))

You can view this as factoring out the common argument from the getter and the setter to return a pair consisting of the result of retrieving the element, and a setter to put a new value back in. This offers the computational benefit that the 'setter' here can recycle some of the work used to get the value out, making for a more efficient 'modify' operation than in the fclabels definition, especially when accessors are chained.

There is also a nice theoretical justification for this representation, because the subset of 'Lens' values that satisfy the 3 laws stated in the beginning of this response are precisely those lenses for which the wrapped function is a 'comonad coalgebra' for the store comonad. This transforms 3 hairy laws for a lens l down to 2 nicely pointfree equivalents:

extract . l = id
duplicate . l = fmap l . l

This approach was first noted and described in Russell O'Connor's Functor is to Lens as Applicative is to Biplate: Introducing Multiplate and was blogged about based on a preprint by Jeremy Gibbons.

It also includes a number of combinators for working with lenses strictly and some stock lenses for containers, such as Data.Map.

So the lenses in data-lens form a Category (unlike the lenses package), are Haskell 98 (unlike fclabels/lenses), are sane (unlike the back end of data-accessor) and provide a slightly more efficient implementation, data-lens-fd provides the functionality for working with MonadState for those willing to step outside of Haskell 98, and the template-haskell machinery is now available via data-lens-template.

Update 6/28/2012: Other Lens Implementation Strategies

Isomorphism Lenses

There are two other lens encodings worth considering. The first gives a nice theoretical way to view a lens as a way to break a structure into the value of the field, and 'everything else'.

Given a type for isomorphisms

data Iso a b = Iso { hither :: a -> b, yon :: b -> a }

such that valid members satisfy hither . yon = id, and yon . hither = id

We can represent a lens with:

data Lens a b = forall c. Lens (Iso a (b,c))

These are primarily useful as a way to think about the meaning of lenses, and we can use them as a reasoning tool to explain other lenses.

van Laarhoven Lenses

We can model lenses such that they can be composed with (.) and id, even without a Category instance by using

type Lens a b = forall f. Functor f => (b -> f b) -> a -> f a

as the type for our lenses.

Then defining a lens is as easy as:

_2 f (a,b) = (,) a <$> f b

and you can validate for yourself that function composition is lens composition.

I've recently written on how you can further generalize van Laarhoven lenses to get lens families that can change the types of fields, just by generalizing this signature to

type LensFamily a b c d = forall f. Functor f => (c -> f d) -> a -> f b

This does have the unfortunate consequence that the best way to talk about lenses is to use rank 2 polymorphism, but you don't need to use that signature directly when defining lenses.

The Lens I defined above for _2 is actually a LensFamily.

_2 :: Functor f => (a -> f b) -> (c,a) -> f (c, b)

I've written a library that includes lenses, lens families, and other generalizations including getters, setters, folds and traversals. It is available on hackage as the lens package.

Again, a big advantage of this approach is that library maintainers can actually create lenses in this style in your libraries without incurring any lens library dependency whatsoever, by just supplying functions with type Functor f => (b -> f b) -> a -> f a, for their particular types 'a' and 'b'. This greatly lowers the cost of adoption.

Since you don't need to actually use the package to define new lenses, it takes a lot of pressure off my earlier concerns about keeping the library Haskell 98.

참고URL : https://stackoverflow.com/questions/5767129/lenses-fclabels-data-accessor-which-library-for-structure-access-and-mutatio

반응형