I wrote my first Pyret program. Comments?

233 views
Skip to first unread message

Barry Brown

unread,
Dec 5, 2013, 4:26:10 PM12/5/13
to pyret-...@googlegroups.com
I'm new to the group, having just learned of this project.

I took a couple of Python functions I use in one of my courses and ported them to Pyret. It took about half an hour to dig through the docs to figure it out.

data Pair:
  | pair(fst :: Number, snd :: Number)
where:
  pair(5,6).fst is 5
  pair(5,6).snd is 6
end

# Extended Euclidean GCD
# Computes a*x + b*y = gcd(a, b) and returns (x, y)
fun egcd(a :: Number, b :: Number) -> Pair:
  if b == 0:
    pair(1, 0)
  else:
    q = (a / b).truncate()             # Integer division
    r = a.modulo(b)
    p = egcd(b, r)                     # (s, t) = egcd(b, r)
    pair(p.snd, p.fst - (q * p.snd))   # (t, s - (q * t))
  end
where:
  egcd(7, 21) is pair(1, 0)
  egcd(13, 40) is pair(-3, 1) 
end

fun multiplicative-inverse(a, b):
  egcd(a, b).fst.modulo(b)
where:
  multiplicative-inverse(13, 40) is 37
end

Is there anything I could have done better? For instance, is there built-in support for tuple-like constructs without having to define a type?

Is there a way to define my own operators? In SML, for example, I can define a function called "mod" and then turn it into a binary operator so that "5 mod 2" is the same as mod(5,2) which in turn becomes 5.modulo(2).

Justin Pombrio

unread,
Dec 5, 2013, 4:39:45 PM12/5/13
to pyret-...@googlegroups.com
Great to have you!

That looks like pretty idiomatic Pyret to me. Pyret does not yet have built-in tuples, though I think there was previous discussion about adding them. I don't remember what the conclusion was, but it's on our minds.

Pyret doesn't have user-defined operators, though there is operator overloading if, say, you want to define '+' on your own data types.

Justin


--
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/groups/opt_out.

Frank Goodman

unread,
Dec 5, 2013, 4:53:25 PM12/5/13
to pyret-...@googlegroups.com
Pyret has built-in doc strings for documenting functions, which you may find useful.

For instance,

# Extended Euclidean GCD
# Computes a*x + b*y = gcd(a, b) and returns (x, y)
fun egcd(a :: Number, b :: Number) -> Pair:
  ...

can be changed to:

fun egcd(a :: Number, b :: Number) -> Pair:
  doc: "Extended Euclidean GCD: Computes a*x + b*y = gcd(a, b) and returns (x, y)"
  ...

--
Frank Goodman
Brown University '16

Joe Gibbs Politz

unread,
Dec 5, 2013, 5:21:46 PM12/5/13
to pyret-...@googlegroups.com
On Thu, Dec 5, 2013 at 4:26 PM, Barry Brown <ba...@barrybrown.me> wrote:
> I'm new to the group, having just learned of this project.

Welcome, and thanks for trying out Pyret!

> I took a couple of Python functions I use in one of my courses and ported
> them to Pyret. It took about half an hour to dig through the docs to figure
> it out.

What could have been better in the docs? I know there's much room for
improvement there, but I'm wondering what required digging in
particular for you.

> data Pair:
> | pair(fst :: Number, snd :: Number)
> where:
> pair(5,6).fst is 5
> pair(5,6).snd is 6
> end
> [...]

Nice!

> Is there anything I could have done better? For instance, is there built-in
> support for tuple-like constructs without having to define a type?

There isn't a Pyret-defined Tuple or Pair type right now. We deferred
this decision to see what kinds of patterns would come up in our
classes' code this semester, rather than urging pair-based patterns
from the get-go. There are clear benefits to having tuples built-in
(the standard library can know about them and provide zip- and
unzip-like functions, for instance). Even if we add them, which we
very well might, I'm conflicted on having special syntax for them:
parentheses cause an awful lot of syntactic ambiguity, and there's a
lot of competition for the rest of simple ASCII.

Often, what I do in a situation like this is create an anonymous
object, which lets me name the fields of my tuple. For example, I
might not define Pair, and write instead:

# Computes a*x + b*y = gcd(a, b) and returns (x, y)
fun egcd(a :: Number, b :: Number) -> { x :: Number, y :: Number }:
if b == 0:
{ x: 1, y: 0 }
else:
q = (a / b).truncate() # Integer division
r = a.modulo(b)
p = egcd(b, r) # (s, t) = egcd(b, r)
{ x : p.y, y: p.x - (q * p.y) } # (t, s - (q * t))
end
where:
egcd(7, 21) is { x: 1, y: 0 }
egcd(13, 40) is { x: -3, y: 1 }
end

This way, I get more descriptive names than fst and snd, or left and
right. There are obvious tradeoffs: the standard library won't be
able to unzip a list of these egcd results for me, for example.

> Is there a way to define my own operators? In SML, for example, I can define
> a function called "mod" and then turn it into a binary operator so that "5
> mod 2" is the same as mod(5,2) which in turn becomes 5.modulo(2).

Pyret has a fixed set of operators right now, and there aren't plans
to add user-defined ones, though we're open to hearing about use cases
for them. The closest thing Pyret has to programmer-defined infix is
the ^ operator. This lets you use functions in a position that
visually resembles a method call. For example:

check:
13^egcd(40) is { x: -3, y: 1 }
# v1^f(v2) is equivalent to f(v1, v2)
end

This is a slightly different use case than you might have in mind, though.

Cheers!
Joe P.

Barry Brown

unread,
Dec 5, 2013, 7:24:02 PM12/5/13
to pyret-...@googlegroups.com


On Thursday, December 5, 2013 2:21:46 PM UTC-8, Joe Gibbs Politz wrote:
On Thu, Dec 5, 2013 at 4:26 PM, Barry Brown <ba...@barrybrown.me> wrote:

What could have been better in the docs?  I know there's much room for
improvement there, but I'm wondering what required digging in
particular for you.

> data Pair:
>   | pair(fst :: Number, snd :: Number)
> where:
>   pair(5,6).fst is 5
>   pair(5,6).snd is 6
> end
> [...]


It was the definition of the Pair type that took the longest to figure out. Leaning on my SML and Haskell experience, I saw the bar as meaning "or", as in "A BTree is empty or a node". But I didn't want my Pair to be a "something or something", I wanted it to be a "something AND something." Staring at the grammar for about 15 minutes and working some examples in my head led me to realize that the bar actually means "Avast, here be a constructor!" and that I don't need to define the attribute elements separately, like in Java classes. It seems to be more like Python or Ruby where you can just add instance attributes to an object ad-hoc.

I remember seeing some definitions for "point" and "make-point" in the docs which I glommed onto because I recognized that from HtDP's posn, but the examples shown ended up bearing little resemblance to my Pair type. Again, it was working through the grammar of a data-expr that led me to the right syntax.

 
> Is there anything I could have done better? For instance, is there built-in
> support for tuple-like constructs without having to define a type?


Often, what I do in a situation like this is create an anonymous
object, which lets me name the fields of my tuple.  For example, I
might not define Pair, and write instead:

# Computes a*x + b*y = gcd(a, b) and returns (x, y)
fun egcd(a :: Number, b :: Number) -> { x :: Number, y :: Number }:
  if b == 0:
    { x: 1, y: 0 }
  else:
    q = (a / b).truncate()             # Integer division
    r = a.modulo(b)
    p = egcd(b, r)                     # (s, t) = egcd(b, r)
    { x : p.y, y: p.x - (q * p.y) }   # (t, s - (q * t))
  end
where:
  egcd(7, 21) is { x: 1, y: 0 }
  egcd(13, 40) is { x: -3, y: 1 }
end

This way, I get more descriptive names than fst and snd, or left and
right.  There are obvious tradeoffs: the standard library won't be
able to unzip a list of these egcd results for me, for example.

OK, that makes sense. I did see something about braces in the docs and that they were related to objects (http://www.pyret.org/#examples), but I didn't see any samples that seemed applicable to my situation. 

As for the competitive ASCII character space... How about extending to Unicode? Surely there must be some symbols there you could use to represent tuples or sets or whatever else you want to bake in.

Shriram Krishnamurthi

unread,
Dec 5, 2013, 8:28:53 PM12/5/13
to pyret-...@googlegroups.com
Barry, thanks a lot for the feedback.

We've held off on Unicode for a few different reasons, mainly that it
works better in theory than in practice: for parsing, entering on a
variety of keyboard, etc. For instance, the closer we can hew to
ASCII, the easier it is to type on an iPad keyboard; the farther we
get from it, the more difficult. If some standard operator of the
language requires a four-touch sequence, that would be awful.

Joe, I wonder if it doesn't make sense to have documentation sections
for features we _don't_ have, explaining how to obtain them. For
instance, it would eminently reasonable to have a section labeled
"Tuples" that says sorry, we don't have them for now, and here's why;
but here are at least two different ways to obtain the
equivalent. Barry won't be the last person to look for them.

In case it wasn't clear, the reason Joe said

> data Pair:
> | pair(fst :: Number, snd :: Number)
> where:
> pair(5,6).fst is 5
> pair(5,6).snd is 6
> end
> [...]

Nice!

is because this is precisely the sort of thing we hope for people to
do. As you know, the HtDP design recipe says that once you've written
your data definition, you should then write examples of your data to
make sure you know how to use them. In principle, this extends to
tests of using them too, as you have done.

This becomes even more interesting when you use refinements. Then
you'd like to write examples and make sure they really do conform to
your refinements, before you build up false intuitions.

The other thing we'd like to do -- we're still trying to figure out a
good scoping semantics to enable this -- is for examples bound in the
data definition to be available in function test cases. Eg,

data Pair:
...
where:
p0 = pair(0, 0)
p1 = ...
p2 = ...
end

followed by

fun distance(p :: Pair, q :: Pair): ...
where:
distance(p0, p1) is ...
distance(p1, p2) is ...
...
end

Shriram

Barry Brown

unread,
Dec 9, 2013, 2:09:17 PM12/9/13
to pyret-...@googlegroups.com, s...@cs.brown.edu


On Thursday, December 5, 2013 5:28:53 PM UTC-8, Shriram Krishnamurthi wrote:

In case it wasn't clear, the reason Joe said

  > data Pair:
  >   | pair(fst :: Number, snd :: Number)
  > where:
  >   pair(5,6).fst is 5
  >   pair(5,6).snd is 6
  > end
  > [...]

  Nice!

is because this is precisely the sort of thing we hope for people to
do. As you know, the HtDP design recipe says that once you've written
your data definition, you should then write examples of your data to
make sure you know how to use them. In principle, this extends to
tests of using them too, as you have done.

I come well-trained. :)

It seems to me that Pyret beginners are going to have trouble with the linguistically-similar "=", "==", and "is", especially when contained in the where: section. For example, this works:

data Pair:
  | pair(fst :: Number, snd :: Number)
where:
  p0 = pair(5,6)
  p1 = pair(9,4)
  p0 == pair(5,6)
  p1 == pair(9,4)
  p0 is pair(5,6)
  p1 is pair(9,4)
end

A novice might assume the "==" expressions are adequate tests and omits the "is" statements. Then wonders why the compiler is throwing a warning. In my experience, novices either completely ignore the compiler warnings/errors or assume the compiler is wrong. Perhaps since today's technology is so quirky, they've learned it's OK (and even correct) to blame the computer when things don't work.

Maybe in the where section you should allow only bindings and tests.

The other thing we'd like to do -- we're still trying to figure out a 
good scoping semantics to enable this -- is for examples bound in the
data definition to be available in function test cases. Eg,

  data Pair:
    ...
  where:
    p0 = pair(0, 0)
    p1 = ...
    p2 = ...
  end

followed by

  fun distance(p :: Pair, q :: Pair): ...
  where:
    distance(p0, p1) is ...
    distance(p1, p2) is ...
    ...
  end

You're right; that's potentially very ugly.

How about referring to p0 as Pair.p0?

where:
  distance(Pair.p0, Pair.p1) is ...
  ...
end


Barry 

Justin Pombrio

unread,
Dec 9, 2013, 3:15:53 PM12/9/13
to pyret-...@googlegroups.com
On Mon, Dec 9, 2013 at 2:09 PM, Barry Brown <ba...@barrybrown.me> wrote:

It seems to me that Pyret beginners are going to have trouble with the linguistically-similar "=", "==", and "is", especially when contained in the where: section. For example, this works:

data Pair:
  | pair(fst :: Number, snd :: Number)
where:
  p0 = pair(5,6)
  p1 = pair(9,4)
  p0 == pair(5,6)
  p1 == pair(9,4)
  p0 is pair(5,6)
  p1 is pair(9,4)
end

Just a note: I have seen a student do this and expect 'p0 == pair(5, 6)' to act as a test. I think the pattern they're expecting is that an expression of type boolean in a where block acts as a test that succeeds when it evaluates to 'true', which isn't entirely unreasonable.

Joe Gibbs Politz

unread,
Dec 9, 2013, 4:43:39 PM12/9/13
to pyret-...@googlegroups.com
> Just a note: I have seen a student do this and expect 'p0 == pair(5, 6)' to
> act as a test. I think the pattern they're expecting is that an expression
> of type boolean in a where block acts as a test that succeeds when it
> evaluates to 'true', which isn't entirely unreasonable.

I've seen that, too. We actually have a fair amount of data from
writing Pyret all semester, so we can start writing better warnings
for this and other common slip-ups.

We could also try to design the syntax around this problem, but there
is only so much ASCII, so rough spots are bound to show up sometimes.
We just need the Pyret environment to guide students to the right
constructs.

I think if this particular case does warrant a change, the "=="
operator is the right thing to be taking a hard look at. It's
something that I don't have much of a justification for aside from its
presence in other languages.

Shriram Krishnamurthi

unread,
Dec 11, 2013, 8:29:11 PM12/11/13
to pyret-...@googlegroups.com
> I think if this particular case does warrant a change, the "=="
> operator is the right thing to be taking a hard look at. It's
> something that I don't have much of a justification for aside from its
> presence in other languages.

... AND with overloaded meanings, so it's not even clear that relying
on their past experience is wise.

Shriram
Reply all
Reply to author
Forward
0 new messages