New to pyret and can't understand Data Definition

瀏覽次數:53 次
跳到第一則未讀訊息

jiamo

未讀,
2016年4月29日 晚上11:31:582016/4/29
收件者:Pyret Discuss

data Point:
  | point(x :: Number, y :: Number)end

fun distance-to-origin(p :: Point) -> Number:
  doc: "Produces the distance from the origin to the given point."where:
  distance-to-origin(point(0, 0)) is 0
  distance-to-origin(point(3, 4)) is 5
  distance-to-origin(point(-3, -4)) is 5end

So point here  is something like a  construct function, but in distance-to-origin name space or scope point don't need know
`point` is `Point` . 

What is point ? I am confusing the relationship between `Point` and `point`. How can I now point will construct a Point. If Point is defined in different place?

Mo Jia

未讀,
2016年4月29日 晚上11:51:392016/4/29
收件者:pyret-...@googlegroups.com
I can do something like:


data Point:
| point(x :: Number, y :: Number)
| point3(y :: Number, x :: Number, z :: Number)
end


cases (Point) point3(0, 1 ,2 ):
| point3(x, y, z) => x + y + z
end

Data and Data constructor function may be need same name. x =
Point.point(2, 3) or just x = Point(2, 3).
If there is a func name different with Data name. It may cause
misunderstanding. How can I know is a data constructor or is a normal
function name.
> --
> You received this message because you are subscribed to the Google Groups
> "Pyret Discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to pyret-discus...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Shriram Krishnamurthi

未讀,
2016年4月30日 上午9:06:252016/4/30
收件者:pyret-...@googlegroups.com
Hi Mo Jia,

Thanks for your interest and your question.

It is a bit complicated to explain this in the setting of

  data Point:
    | point ...
  end

i.e., when there is only one “branch”. So let's consider an example with more than one branch. Also, it may be useful to not talk about points but a different datatype entirely. For instance:

  data Animal:
    | lion(...)
    | whale(...)
  end

Sometimes we care about Animals in a generic sense: e.g.,

  zoo :: Set<Animal> = [set: ...]

but other times we want to do something specific:

  fun what-it-eats(a :: Animal):
    cases (Animal) a:
      | lion ...
      | whale ...
    end
  end

because they eat very different things.

=====

So now, let's get back to points with more than one kind, just like you wrote, but I'll use slightly different names:

  data Point:
    | point2(x, y)
    | point3(x, y, z)
  end

What this says is that there are two kinds of Points: there are 2d points (point2) and 3d points (point3). When we make them, we have to be clear on which kind of point we're making:

  point2(5, 12)
  point3(14, 25, 1)

When we're computing with them, we have a choice. Sometimes, we only care that there's a Point, no matter which kind. In these situations, we can use the Point annotation. We can even do computations that depend on only what's common:

  fun xydist(p :: Point): ... p.x ... p.y ... end

This will work with both point2 and point3. However, there are times when we want to truly different things. Then we have to use cases:

  fun dist-from-0(p :: Point):
    cases (Point) p:
      | point2(x, y) => ... x ... y ...
      | point3(x, y, z) => ... x ... y ... z ...
    end
  end

=====

Now in the special case where there is only one kind of thing, it looks a bit more odd. For instance, maybe we have only a very generic notion of animal:

  data Animal:
    | animal(...)
  end

Then we use animal(...) to make Animal values, but Animal represents ALL the kinds of them — of which there is only one (so there's no real meaningful difference between them). Similarly, with Point and point.

Does that help any?

Shriram

Mo Jia

未讀,
2016年4月30日 上午11:13:232016/4/30
收件者:pyret-...@googlegroups.com
It is helpful.

I am tring to understand in lexical parsing way.
Which I mean.

data Point1:
| point(x, y)
end

data Point2:
| point(x, y)
end

This code in pyret will cause error: You need to pick a different name
for one of them.


But In my understand. Point1 Point2 is different data type. I can
have same name constructor function to build a point in their type.
And I can using the same name. Because the constructor is in the type
Point2 or Point1. It may not be defined in the same namespace.

Izzet Pembeci

未讀,
2016年4月30日 下午6:30:392016/4/30
收件者:pyret-...@googlegroups.com
If Pyret was statically typed like C++, Java this may be allowed because the context will determine which point(...) to be used:

Point2 p = point(3,7)

but assume now Pyret just sees this:

p = point(3,7)

Then, it can't determine if p is Point1 or Point2.

By the way don't think point(...) as a constructor function. Think them like labels marking the kind of a value which belongs to a particular data type. For instance:

data BinTree:
  | empty
  | node(value :: Number, left :: BinTree, right :: BinTree)
end
As you can see here, the label empty just refers to a special value a BinTree may hold. The label node refers to another set of values which consists of a Number and two other BinTree's. We need these labels to determine what to do when we face with different kind of values a data type can hold:

fun sum-of-values(a-tree):
  cases(BinTree) a-tree:
    | empty => 0
    | node(val, left, right) => val + sum-of-values(left) + sum-of-values(right)
  end
end

Hope this clears some of your confusion.

iZzeT

Joe Gibbs Politz

未讀,
2016年4月30日 晚上10:12:282016/4/30
收件者:pyret-...@googlegroups.com
On Sat, Apr 30, 2016 at 11:13 AM, Mo Jia <life....@gmail.com> wrote:
> But In my understand. Point1 Point2 is different data type. I can
> have same name constructor function to build a point in their type.
> And I can using the same name. Because the constructor is in the type
> Point2 or Point1. It may not be defined in the same namespace.

This is precisely correct.

Point1 and Point2 describe different data types. The only thing
stopping them both being declared in the same module is that Pyret
doesn't allow the same identifier to be bound twice in the same scope.
So there's no restriction that two datatypes cannot define
constructors with the same name: it's strictly a scoping rule.

We could define Point1 and Point2 in different modules, and tell them
apart. Here's an example:

https://code.pyret.org/editor#share=0B32bNEogmncOX0VkbEo0cmU1RFE

That module imports the following one, which defines Point1, which
also has a point constructor:

https://code.pyret.org/editor#share=0B32bNEogmncOODl3bnYxVHNONkE

The tests in the first file show that Point1's point is different from
Point2's point, despite them having the same constructor name and
fields.

On Sat, Apr 30, 2016 at 6:29 PM, Izzet Pembeci <pem...@gmail.com> wrote:
> If Pyret was statically typed like C++, Java this may be allowed because the
> context will determine which point(...) to be used:

Indeed, type information could make this work, though even with the
type checker on, Pyret disallows two constructors with the same name
to be declared in the same scope. This is a conscious decision in
Pyret's design to keep scope simple and avoid errors due to unintended
shadowing and redefinition of names.

> By the way don't think point(...) as a constructor function. Think them like
> labels marking the kind of a value which belongs to a particular data type.

The label perspective is indeed quite useful to understand how "cases"
works in Pyret, thanks for pointing this out.

It's worth mentioning that in Pyret, "point" and other constructors
created by data definitions are actually functions, and can be passed
around or stored in other places as values like any other function.
That said, the label perspective is the right one to take for working
with data in the vast majority of situations.


======

As a little aside, I got curious about the behavior of this in a
closer cousin of Pyret. In OCaml, it's not an error to write:

type point1 =
| Point of int * int

type point2 =
| Point of int * int

let (x : point1) = Point(5, 6);;
let (y : point2) = Point(2, 4);;


There's an interesting limit to the lengths type inference will go
here, though. If we write:

let z = Point(5, 6);;
let (w : point1) = z;;

We get an error:

Error: This expression has type point2 but an expression was expected of type
point1

Because the second Point constructor is chosen by type inference,
ascribing the point2 type to z, and causing the binding on the next
line to fail as a result.

Izzet Pembeci

未讀,
2016年5月1日 下午5:34:562016/5/1
收件者:pyret-...@googlegroups.com
This is a conscious decision in Pyret's design to keep scope simple and avoid errors due to unintended
shadowing and redefinition of names.


I think this is a wise decision. The confusion for the programmer and increased language implementation complexity just doesn't worth the advantages of allowing cases like Mo mentioned.

It's worth mentioning that in Pyret, "point" and other constructors created by data definitions are actually functions, and can be passed
around or stored in other places as values like any other function.


This is also very neat. It opens some interesting possibilities.

Thanks Joe for reclarifying/correcting my clarifications. Just discovered Pyret and didn't dig into the inner workings much yet.

iZzeT

回覆所有人
回覆作者
轉寄
0 則新訊息