使用makePrisms例子 [英] Use cases of makePrisms with examples

查看:126
本文介绍了使用makePrisms例子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不清楚makeLense和makePrisms之间的区别吗?



我知道,当我们想要访问一个嵌套的结构/数据,然后像这样使用makeLense:

  data Point = Point {_x :: Int,_y :: Int} 
data Test = Test {_name :: String,_position :: Point}

makeLenses''Point
makeLenses''Test

然后我们可以访问或修改测试或点的组件。例如,我们定义一个函数:

  modify :: Test  - >测试
modify = over(position。x)(* 8)



  let t1 = Test {_name =Me,_position = Point {_x = 3,_y = 8}} 

然后

 修改t1 

将会是:

  Test {_name =Me,_position = Point {_x = 24,_y = 8}} 

但是,我不知道何时以及如何使用makePrisms! 为了理解这个,你必须理解类型,类型变量,代数数据类型(sum和product类型),并且你还必须理解类型类和 Functor 特别是类型类。如果您不理解这些内容,请将此页面加入书签,然后关闭并了解它们,可能使用我帮助完成此目的的资源来说明和解释这些基本知识: http://happylearnhaskelltutorial.com



所以在我们找到Prisms之前,你首先需要知道什么一个镜头是。

镜头通常被描述为一个功能性的getter / setter,但更多的是关于实现等等。



我想现在在这里尝试一下描述实验。



说我有一页非常小的文字,上面有一些文字。现在,我递给你一张与该页面大小相同的纸板,只有它有一个用于放大镜的孔,以便专注于特定的单词。这个词在该页面的特定位置。



所以我们有这两样东西:一个页面和一个没有放大镜的放大镜卡片......如果我们放入杯子,它将专注于页面上的特定位置。现在有人会出现另一页上有不同的文字,但与第一页相同的布局。



很容易形象,你可以拿着那张牌,把它在新的页面上,它将重点放在同一地点的不同词。



除了放大镜,还有一个特殊的橡皮擦/打印机玻璃,当你把它放入卡片时,你可以擦除并在该页面上输入文字。

所以现在将这个应用于Lens,你可以看到这个例子如何我们将一段数据(一页文本),一个适合该数据的镜头(一张带有与页面形状相匹配的孔的卡片)和一个可以获取或设置的函数(或者可能其他的东西)(也就是放大镜或橡皮擦/打印机玻璃或其他类型的眼镜),从这一点上,我们可以查看较小的一段数据(这个词)在更大的一块数据(页面)中提取它...或者我们可以 set 一个不同的匹配一块数据进入该页面的现货...

lens :: :: Functor f => (s→a)→> (s→b→t)→> (a - > f b) - > s - > f t



这个函数做什么?它从某些功能创建镜头。现在我们有了上面的框架来了解镜头是什么,我们可以去掉这个功能。我告诉你, s 变量表示状态,它对应于镜头将聚焦在其内的纸张类型。接下来,我可以说 a 类型变量对应于镜头将聚焦在页面上的单词。那么 b t ?如果我们决定改变 a的值,它们就是变换后的 a s ,并且该更改会更改其类型。



那么, Functor 是什么?我们会发现一段时间。那么,首先让我们制作一个镜头,使其具有实用性。所以,回到我们的镜头函数,它的第一个参数是getter函数,第二个参数是setter函数(因此是类型)。然后还有另外一个说法。那么,因为Haskell函数是curried,实际上是返回类型:一个函数 s - > f t 。假设我们有一个值列表 [(1,(5,9)),(2,( 3,6))] ,我们想制作一个专注于第二个嵌套元组中第二个值的镜头。这很愚蠢,因为你可以使用 snd。 snd 对吗?是的,你可以,所以这不是一个很好的例子,但所有更好的例子都是更复杂的,所以请耐心等待 - 我们会找到他们,另外,你的 snd.snd 函数也是SET还是应用了一个函数?不,我不这么认为! :)(好吧好吧我知道你可以使用 fmap(const val)来设置,但是它是否也可以改变类型?这个思路实际上就是你最终的结果如果你继续按照Edward Kmett的说法继续它的逻辑结论 - 那就是Functor进来的地方!)

$ p $ s $ s $ $ $ $ sndOfSndLens = lens(snd) (x,(y,newZ)))
>那么我们得到了什么?我们得到了这种类型的函数 sndOfSndLens :: Functor f => (a - > f t2) - > (t,(t1,a)) - > f(t,(t1,t2))



让我们来看看我们的值: map(view sndOfSnd )[(1,(5,9)),(2,(3,6))] - > [9,6] Nice !这是有效的...让我们设置新的值: map(set sndOfSnd 2000)[(1,(5,9)),(2,(3,6))] - > [(1,(5,2000)),(2,(3,2000))] 好的...



如果只是getters或setter,那很无聊,但也有一个叫做 over 的函数,它会使用一个镜头和一个转换函数并运行转换镜头焦点的功能......所以让我们从中减去10: map(通过sndOfSnd(flip( - )10))[(1,(5,9)),( 2,(3,6))] - > [(1,(5,-1)),(2,(3,-4))] 很酷!好吧,我会让你阅读镜头文档的其余部分以了解所有其他功能,因为它们很深并且镜头组成,而且你可以用它们做各种其他的事情。

b

Prism



我答应我们会到Prism,看看我们在这里... A镜头是一种特殊的光学元件(同时,也有时候,也总是混淆地提及整套光学元件,所以请留意),棱镜是另一种光学元件,非常特别。如果Lens是一种聚焦于 product 代数数据类型的特定部分的光学元件,则Prims对 sum 类型。这就是为什么我们能够制作一个关于配对((,))的镜头,因为它们是产品类型...即他们将两种类型合并为一种。透镜让你专注于一件,或通过这些作品的路径。顺便说一下,我们上面创建的镜头可以很容易地通过组合更通用的内置镜头来定义: _2。 _2 。还有我们正在讨论的所有镜头功能的操作员版本。他们看起来很疯狂,但他们有一个逻辑。阅读关于它们的信息!



通过总和类型,Prism可让您专注于路径。什么是一个很好的例子?那么,让我们假设我们已经考虑了或者数据类型。它是或者是b ,它被定义为 data或者a b =左a |正确的b 。所以有一个相应的 prism 函数,可以让我们构建一个与上述值相同的Prism。如果我们使用内置的 _Left ,它将重点放在的左侧,但是我们只有一个右侧10 值?让我们看看......但首先,我们应该让你知道我们不能再使用 view ,因为它可能不起作用,所以我们需要使用预览,它会返回一个可能失败的值(对不起,破坏者!):

preview _Left(Left 10) - > 只要10 然后一? preview _Left(Right 10) - > Nothing


$ b $ set 函数可以很好地工作,因为如果它没有意义,它可以默默地失败: set _Left 30(Left 10) - > Left 30 。当它不起作用时会发生什么? set _Right 30(Left 10) - > Left 10 没错。



酷...希望这可以解释镜头和底片。他们是两个非常有用的光学元件。镜头库是完整的,所以我鼓励你看看它们。

原始问题怎么样?



原始问题是关于 makeLenses makePrisms 。这些是模板haskell表达式(也就是说,它们是元编程/类似于宏,但是键入宏),并且它们允许您从自己的数据类型自动构建自己的镜头和/或棱镜。希望现在当你选择一个而另一个不同时更有意义。这会给你至少一个他们不同的感觉。要真正理解,你应该阅读文档,并查找所有其他可能的功能和光学元件。



欢迎来到令人惊叹的镜头世界!


It's not clear to me the difference between makeLense and makePrisms?

I'm aware that when we want to access a nested structure/data then use makeLense like this:

data Point = Point { _x :: Int, _y :: Int}
data Test= Test {_name :: String, _position :: Point} 

makeLenses ''Point
makeLenses ''Test

Then we can access or modify the component of Test or Point. For instance, we define a function:

modify :: Test -> Test
modify = over (position . x) (*8)

So we can have:

let t1 = Test {_name= "Me", _position = Point {_x = 3, _y = 8}}

then

modify t1

would be:

Test {_name = "Me", _position = Point {_x = 24, _y = 8}}

But, I don't know when and how to use makePrisms in the above example!

解决方案

In order to understand this, you must understand types, type variables, algebraic data types (sum and product types) and you must also understand typeclasses and the Functor typeclass in particular. If you don't understand these things, bookmark this page, then go off and understand them, possibly with this resource that I helped work on for precisely this purpose, to illustrate and explain these basics: http://happylearnhaskelltutorial.com

So before we get to Prisms, you first need to know what a Lens is.

A Lens is often described as a functional getter/setter, but that's more about the implementation and so forth.

I'd like to try an experiment of description here with you now.

Say I have a page of very small text with some words on it. Now, I hand you a piece of cardboard the same size as that page, only it has hole for a magnifying glass set up to focus on a particular word. That word is in a specific place in that page.

So we have these two things: a page, and a magnifying glass "card" without a magnifying glass... if we put a glass in, it focusses on a particular place on the page. Now someone comes along with another page with different words on it, but in the same layout as the first page.

It's easy to image that you can take that card, put it on the new page and it will focus on the different word in that same place.

As well as a magnifying glass, you also have a special "eraser/printer" glass that when you put it into the card allows you erase and type words over that page.

So to now apply this to Lens, you can see with this example how if we take one piece of data (a page of text), a lens that fits that data (a card with a hole in it that matches the shape of the page) and a function that can "get" or "set" (or maybe other things) (that is, the magnifying glass or the eraser/printer glass, or other types of glasses), well, from this we can view the smaller piece of data (the word) in the larger piece of data (the page) to extract it... or perhaps we can set a different matching piece of data into the spot on that page...

lens :: :: Functor f => (s -> a) -> (s -> b -> t) -> (a -> f b) -> s -> f t

What does this function do? It creates a lens from some functions. Now we have the framework above to understand what a lens is, we can undertsand this function. I say to you that the s variable stands for "state" and it corresponds to the type of the piece of paper that is the context that the lens will focus within. Next I can say the a type variable corresponds to the word on the page that the lens will focus on. What about b and t? They are the transformed a and s values if we decide to change the value of the a, and that change changes its type.

What is the Functor for then? we'll find out in a while. Well, first up let's make a lens to make this practical. So, back to our lens function, its first argument is the "getter" function, the second argument is the "setter" function (hence the types). Then there's one other argument. Well, because Haskell functions are curried that's actually the return type: a function s -> f t. Let's make that lens now.

Let's say we have a list of values [(1,(5,9)), (2,(3,6))] and we'd like to make a lens that focusses on the second value in the second nested tuple. That's silly, though, because you could just use snd . snd right? Yeah, you could, so it's not a great example, but all the better examples are more complex, so bear with me - we'll get to them, and besides, can your snd.snd function also SET, or have a function applied to it? No, I didn't think so! :) (okay okay I know you could use fmap (const val) to set, but can it also change the type? well this line of thought is actually what you end up with Lens with if you continue it to its logical conclusion as Edward Kmett did - that's where the Functor comes in!)

sndOfSndLens = lens (snd.snd) (\(x,(y,z)) newZ -> (x,(y,newZ)))

So what did we get? we got a function of this type sndOfSndLens :: Functor f => (a -> f t2) -> (t, (t1, a)) -> f (t, (t1, t2)).

So let's get our values out: map (view sndOfSnd) [(1,(5,9)), (2,(3,6))] -> [9,6] Nice! That works... let's set new values in: map (set sndOfSnd 2000) [(1,(5,9)), (2,(3,6))] -> [(1,(5,2000)),(2,(3,2000))] okay fine...

If it was just getters or setters, that's boring, but there's also a function called over which will take a lens and a transforming function and run that transformation on the function of the focus of the lens... so let's subtract 10 from each: map (over sndOfSnd (flip (-) 10)) [(1,(5,9)), (2,(3,6))] -> [(1,(5,-1)),(2,(3,-4))] that's cool! well I'll let you read the rest of the lens documentation to understand all the other functions, because they're deep and lenses compose, and you can do all sorts of other things with them, too.

Prism

I promised we'd get to Prism, and look we're here... A Lens is a specific "optic" (while also, sometimes confusingly referring to the entire set of optics in total, too, so watch out for that), and Prism is another optic, thankfully completely specific. If a Lens is an optic that works for focussing on a particular part of a product algebraic data type, then a Prims does the same thing for sum types. That's why we were able to make a lens that talked about pairs ((,)) because they're a product type... that is they combine two types into one. Lens lets you focus on one piece, or a path through those pieces. By the way, our lens we created above can easily be defined by composing the more generic built in lenses: _2 . _2. There are also operator versions of all the lens functions we were talking about. They look pretty crazy, but they have a logic to them. Read up about them!

Prism lets you focus on one path through a sum type. What's a good example? Well, let's say we have think about the Either data type. It's Either a b where it's defined as data Either a b = Left a | Right b. So there's a corresponding prism function that lets us build a Prism the same as our above value. What happens if we use the built in _Left which focusses on the left side of an Either, but we only have a Right 10 value? Let's see... but first, we should let you know that we can't use view any more because it might not work, so we need to use preview which will return a value that might fail (sorry, spoilers!):

preview _Left (Left 10) -> Just 10 and then the Right one? preview _Left (Right 10) -> Nothing. Okay that's sweet.

The set function works fine, because it can fail silently if it doesn't make sense: set _Left 30 (Left 10) -> Left 30. What happens when it doesn't work? set _Right 30 (Left 10) -> Left 10 that's right, nothing.

Cool... hopefully this explains Lenses and Prims. They're two really useful optics. The Lens library is full of them so I encourage you to look at them

What about the original question tho?

The original question was about makeLenses and makePrisms. These are template haskell expressions (that is, they're meta programming / similar to macros, but typed macros), and they let you automatically build your own lenses and/or prisms from your own data types. Hopefully it makes more sense now when you would pick one over the other and how they differ. This will give you at least an inkling into how they're different. To really understand, you should read the documentation and look up all the other functions and optics that are possible.

Welcome to the amazingly cool world of Lenses!

这篇关于使用makePrisms例子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆