Start working on homework 4 discussion post.
This commit is contained in:
parent
035ee14144
commit
1626c6d535
185
HW4.md
Normal file
185
HW4.md
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
# Benjamin's Solution
|
||||||
|
Looks like Ben opted to literally translate the `Prog` nonterminal into Haskell. This is perfectly valid: if anything, it's a more faithful translation of the syntax. However, as you probably know, the definition is isomorphic to a list:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Prog = Empty | Stmt Stmt Prog
|
||||||
|
data List Stmt = Nil | Cons Stmt (List Stmt)
|
||||||
|
```
|
||||||
|
|
||||||
|
So, you could've defined `Prog` to be `[Stmt]`. There are a few small advantages to this, mostly in terms of using Haskell as a metalanguage. Here's a line from my interpreter for this language:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
stmt (Loop p) = prog (cycle p) `catchError` (const $ return ())
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to implement the looping behavior of `Loop`, I used Haskell's built-in `cycle :: [a] -> [a]` function. This function infinitely repeats a list:
|
||||||
|
|
||||||
|
```
|
||||||
|
cycle [1,2,3] = [1,2,3,1,2,3,1,2,3,...]
|
||||||
|
```
|
||||||
|
|
||||||
|
The more compact list notation is another advantage to this approach.
|
||||||
|
|
||||||
|
I appreciated the following comment in the code:
|
||||||
|
|
||||||
|
```
|
||||||
|
- Starting register A at x, set R to zero, and add to R until A passes y (my approach above)
|
||||||
|
- Same as above, but use y-x as the boundary instead of y, and adjust accordingly
|
||||||
|
- Don't use a do loop, and instead hardcode out y-x additions of x+1 each time
|
||||||
|
```
|
||||||
|
|
||||||
|
What about a closed form approach? Something like `(b-a+1)(b+a)/2`. If `*` and `/` were O(1) operations, this would make the whole summation O(1).
|
||||||
|
|
||||||
|
# Owen's Solution
|
||||||
|
Looks like Owen had some trouble with this homework assignment. From what I can tell, the code should not compile; there are a few things here that can be improved.
|
||||||
|
|
||||||
|
## Allowing `Expr` to be `Int`
|
||||||
|
I'm looking at this line of code:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
-- since there is already an int type in Haskell, I think I can consider it already encoded
|
||||||
|
data Expr = Int
|
||||||
|
```
|
||||||
|
|
||||||
|
Unfortunately, this isn't quite right. This creates a constructor (value!) `Int` of type `Expr`. This value `Int` has nothing to do with the type `Int` (integers). Thus, `Expr` can't contain integers like you intended. The proper syntax would be
|
||||||
|
something like:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Expr = Lit Int
|
||||||
|
```
|
||||||
|
|
||||||
|
Unlike the previous version, this will create a constructor called `Lit` with type `Int -> Expr`. _This_ time, the `Int` refers to the type `Int`, and so, `Lit 3` will represent an expression that contains the number `3`.
|
||||||
|
|
||||||
|
## Defining `Reg`
|
||||||
|
Haskell's `type` is a way to create a _type alias_. So, if you have a type you use a lot (perhaps `Maybe String`), you
|
||||||
|
can give it another name:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
type MyType = Maybe String
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, `Maybe String` and `MyType` mean the same thing. For defining `Reg`, you want to use `data`:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Reg = A | B | C
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using `undefined` or `_holes`
|
||||||
|
It's hard to get the whole program compiling in one go. Haskell supports placeholders, which can be used anywhere, and will compile (mostly) no matter what. Of course, they'll either crash at runtime (`undefined`) or not _really_ compile (`_holes`). However, if you have something like:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
while = -- can't quite do this yet
|
||||||
|
|
||||||
|
sumFromTo = ... -- but now there's a parse error!
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use `undefined`:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
while = undefined
|
||||||
|
```
|
||||||
|
|
||||||
|
This way, there's no parse error, and GHC goes on to check the rest of your program. Holes are useful when you're in the middle of a complicated expression:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
foldr _help 1 [1,2,3]
|
||||||
|
```
|
||||||
|
This will almost compile, except that at the very end, Haskell will tell you the type of the expression you need to fill in for `_help`. It will also suggest what you can put for `_help`!
|
||||||
|
|
||||||
|
## Parenthesization
|
||||||
|
This is a small nitpick. Instead of writing `RegStore A (x)`, you would write `RegStore A x`. In this case, you probably wanted `RegStore A (Lit x)` (you do need parentheses here!).
|
||||||
|
|
||||||
|
## Making Type Errors Impossible
|
||||||
|
I'm looking at this part of Part 2:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Stmt = RegStore Reg IntExpr
|
||||||
|
| RegStore Reg BoolExpr
|
||||||
|
| Contitional IntExpr Prog Prog
|
||||||
|
| Contitional BoolExpr Prog Prog
|
||||||
|
| Loop Prog
|
||||||
|
| BreakLoop
|
||||||
|
```
|
||||||
|
|
||||||
|
You were on the right track!
|
||||||
|
|
||||||
|
> I think this should make it so that I have the full functionality of the previous setting but now I have one version for each type of expr?
|
||||||
|
|
||||||
|
You're exactly right. You can represent any program from Part 1 using this syntax. However, the point was: you don't need to! Programs in the form `Conditional IntExpr Prog Prog` are specifcally __broken__! You can't use a number as a condition for `if/then/else`, and `IntExpr` is guaranteed to produce a number! The same is true for `RegStore Reg BoolExpr`: you aren't allowed to store booleans into registers! See this part of the homework description:
|
||||||
|
|
||||||
|
> Note that although the expression language may produce both booleans and integers, the registers may only contain integers.
|
||||||
|
|
||||||
|
So, it is in fact sufficient to leave out the duplicate constructors you have:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Stmt = RegStore Reg IntExpr
|
||||||
|
| Contitional BoolExpr Prog Prog
|
||||||
|
| Loop Prog
|
||||||
|
| BreakLoop
|
||||||
|
```
|
||||||
|
|
||||||
|
Hope all this helps!
|
||||||
|
|
||||||
|
# Jaspal's Solution
|
||||||
|
Notably different in Jaspal's solution from the others in this group is that they used strings for registers instead of a data type definition. I think this was allowed by this homework assignment; however, I think that using a data type is better for our purposes.
|
||||||
|
|
||||||
|
The issue is, if your register is a string, it can be _any_ string. You can have register `"A"`, register `"a"`, register `"Hello World"`, and so on. But our language only has three registers: `A`, `B`, and `R`. If we define a data type as follows:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Reg = A | B | R
|
||||||
|
```
|
||||||
|
|
||||||
|
Then a value of type `Reg` can _only_ be either `A`, `B`, or `R`. This means that we can't represent programs like
|
||||||
|
|
||||||
|
```
|
||||||
|
Hello := 5
|
||||||
|
```
|
||||||
|
|
||||||
|
Which is good!
|
||||||
|
|
||||||
|
This solution also uses variables to encode programs. I think that's a nice touch! It makes some expressions
|
||||||
|
less bulky than they otherwise would be.
|
||||||
|
|
||||||
|
It doesn't look like the code compiles; here are a few things you can do to bring it closer to a working state.
|
||||||
|
|
||||||
|
## Type Names and Constructor Names
|
||||||
|
Type names (`Expr`, `Int`, `Bool`) all _have to_ start with an uppercase letter. Thus, you should change the following line:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data expr
|
||||||
|
```
|
||||||
|
|
||||||
|
To the following:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Expr
|
||||||
|
```
|
||||||
|
|
||||||
|
And so on.
|
||||||
|
|
||||||
|
The exception to this rule is _type variables_ for when things are _polymorphic_. For instance, we have
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Maybe a = Nothing | Just a
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, `a` _is_ lowercase, but tht's because `a` is actually a _placeholder_ for an arbitrary type. You can tell because
|
||||||
|
it also occurs on the left side of the `=` : `data Maybe a`.
|
||||||
|
|
||||||
|
Constructor names also have to start with an uppercase letter. So, you would have:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
data Stmt
|
||||||
|
= Eql Reg Expr
|
||||||
|
| IfElse Expr Prog Prog
|
||||||
|
| Do Prog
|
||||||
|
| Break
|
||||||
|
```
|
||||||
|
|
||||||
|
## String Notation
|
||||||
|
I have made a case that you _shouldn't_ be using strings for this homework. However, you'll probably use them in the future. So, I want to point out: `'A'` does _not_ mean the string "A". It means a single _character_, like `'A'` in C. To create a String in Haskell, you want to use double quotes: `"A"`.
|
||||||
|
|
||||||
|
Other than those two things, it looks like this should work!
|
||||||
|
|
||||||
|
# My Solution
|
||||||
|
My solution to Part 1 is pretty much identical to everyone else's; I opted to use `Prog = [Stmt]` instead of defining a data type, but other than that, not much is different.
|
Loading…
Reference in New Issue
Block a user