Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

For-Comprehension #879

Open
wants to merge 26 commits into
base: development
Choose a base branch
from
Open

Conversation

ElieOaks
Copy link

@ElieOaks ElieOaks commented May 8, 2019

For - Comprehension
A new kind of for-comprehension is being implemented into Encore that replaces the old for-loop.
For-comprehension can:
-- act as a function call :

var x = for y <- someList do
              y
            end

-- iterate over itterable objects, not just Arrays and Ranges:

for x <- linkedList do
        --body
end

-- iterate over multiple data structures in one call:

for x <- [1, 2, 3], y <- ["a", "b", "c"] do
        -- body
end

-- be passed as an argument

foo( for y <- someList do
            y
        end)

The new for-comprehension does not allow:
-- Different kinds of data structures in header

for x <- linkedList, y <- ["a", "b", "c"] do
        -- body
end

-- Range to return a collection. It will compile, but x will be of type unit. Therefor ranges only desugar into foreach.

var x = for y <- [0 .. 10] do
              y
            end

-- A return in the body of the loop to return something outside of it. The bellow example will not return 1.

fun foo() : int
  for x <- [1, 2, 3]
    return x
  end
end

Rather it is equivalent to:

fun foo() : int
  var returnCollection  for x <- [1, 2, 3]
                                        return x
                                   end
 return returnCollection
end

Which of course would break the typing rules, as it returns an array, and not an int.
Under the hood:
The for-comprehension is pure syntactic sugar, and is desugared into combination of calls to the methods map, flatMap, foreach, and if the loop contains break, into maybeForeach. These methods can be found in some of the collections in the standard libraries, such as Array and LinkedList.
the methods map and flatMap are used in combination, when a return collection is expected. Otherwise the method foreach is used, as it's loop has only side effects.

var x = for y <- someList, z <- someListTwo do
              y + z
            end

is desugaraed into:
var x = someListTwo.flatMap(someList.map(y))


for y <- [0 .. 10] do
         print(y)
end

is desugaraed into:
[0 .. 10].foreach(print(y))


for y <- [0 .. 10] do
         print(y)
         if y > 9 then
            break
         end
end

is desugaraed into:

 [0 .. 10].maybeForeach(
  print(y)
  if y > 9 then
     return Nothing
  end
  Just(())
  )


Added compilation steps
The desugaring of for-comprehension depends upon typing information, and is therefore a new step of compilation called TypedDesugarer, and occurs before the optimizations. As the desusaring process breaks down the For-Expr and builds up a sub-AST of new Expr, the AST needs to be re-type checked, and re-capture checked. Therefore the compilation includes Typed Desugaring, as well as Re - Type checking, and Re - Capture checking. When modular type checking, and capure checking becomes available, these compilation steps can hopefully be removed.

Problem with re - type checking
Type checking twice is not possible due to the type checking process of Match clauses. When Match is type checked, it changes the Match-clause so that it violates its own typing rules and cannot be type checked again without throwing an Error. A hack has been created so that programs with for-comprehension can compile and run, but is currently incompatible with the match - type checker. This fork can be merges only when the match type checking problem has been solved.

Arrays
Unlike classes such LinkedList in the standard Library, there is a list of functions, rather than methods that can be applied to arrays. Therefore for-comprehensions that are over arrays are desugared into function calls map, flatMap, foreach, and if the loop contains break, into maybeForeach, rather than into method calls by the same name.

Other changes
This PR has also had to incorporate the PR: Ability to compile pony runtime with gcc 7.x #865. As the latest ubuntu, which this was developed on, runs on gcc 7.
It also desugars ranges into a class RRange, that can be found in the module-file Data. This was in order to have the ability to add methods. As range is a protected word in the compiler, the class has to be named something else, hence the double R:s. As ranges are only used together with for-loops / for-comprehensions, this change is not expected to affect anything else.

Copy link
Contributor

@kikofernandez kikofernandez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, it looks really good. There are only minor comments on code style. The haskell code seems good.

@@ -0,0 +1,10 @@
-- JOY for-comprehension
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is nice to have the name, but I would remove this line :)

@@ -341,6 +341,14 @@ main =
verbose options "== Optimizing =="
let optimizedTable = fmap optimizeProgram capturecheckedTable

-- JOY for-comprehension
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be careful with these ones.

@@ -684,11 +691,10 @@ data Expr = Skip {emeta :: Meta Expr}
name :: Name,
times :: Expr,
body :: Expr}
-- JOY for-comprehension
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to annotate your stuff :)

@@ -600,6 +600,13 @@ data VarDecl =
varType :: Type}
deriving(Eq, Show)

-- JOY for-comprehension
data ForSource =
ForSource { forVar :: Name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

following Encore compiler convention, this should be name, and the Maybe Type should be ty

@@ -389,7 +389,8 @@ desugar FunctionCall{emeta, qname = QName{qnlocal = Name "exit"}
Exit emeta args

-- Abort
desugar FunctionCall{emeta, qname=QName{qnlocal=Name "abort"} , args=[msg]} =
desugar FunctionCall{emeta, qname = QName{qnlocal = Name "abort"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

collectionTyped <- doTypecheck collection
let collectionType = AST.getType collectionTyped
let forVarType = return $ getInnerType collectionType
return fors{forVarType = forVarType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for consistency, the Haskell style does:

fors{... = ...
      ,... = ...}

where
typeCheckSources :: [ForSource] -> TypecheckM [ForSource]
typeCheckSources sourceList = do
typedSources <- mapM typeCheckSource sourceList
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the return type is the result of the mapM, you move the line mapM typCheckSource sourceList to the line above, removing the do and the return typeSources

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you do the comment above, maybe line 1729 is really a mapM typeCheckSource sourceList, and you have to maintain one less function

collection = setType collectionType collectionTyped}

getForVarTypeList sourceList = mapM getForVarType sourceList
getForVarType ForSource{forVar, collection} = do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add the typing to this function, I think it can serve as documentation giving that returning a tuple is not the most common thing for the compiler

| isRangeType collectionType = intType
| otherwise = head $ getTypeParameters collectionType

addIteratorVariable forVarList = extendEnvironmentImmutable forVarList
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line says that addIteratorVariable = extendEnvironmentImmutable. Hence, I would remove this equivalence since it is just a renaming and use the extendXXXX

let forVarType = getInnerType collectionType
return (forVar, forVarType)

getInnerType collectionType
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should handle errors. That is, what if this function receives an IntLiteral. The compiler would crash.
One way to fix this is to continue with the guards and make sure that the compiler does not crash and throws an error. This will help future compiler writers to know that an IntLiteral was not expected. There may be other types that are not expected, not only the IntLiteral

Copy link
Contributor

@kikofernandez kikofernandez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, the changes look mostly good. There is quite some noise by files that should not be there, binaries that have been added, etc. In general, the comments should be pretty straightforward to address. If you have questions, continue in the local thread :)

Thanks for your work!

critik Outdated
@@ -0,0 +1,9 @@
The first slide after introduction: Have a picture?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong file? This file is not needed here.

@@ -0,0 +1,15 @@
typeCheckSource sourceType fors@(ForSource{fsTy, collection}) = do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is Haskell but uses the Encore file extension.
I am not sure what this is :)

@@ -0,0 +1,45 @@
--These are preliminary for-comprehension tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Encore does not yet return multiple errors.
Could you instead move each of these tests to their own files (and create their own .fail files as well)?

@@ -0,0 +1,36 @@
active class Equal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure on what is this file testing.
Thanks

end

end
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a forComp/tryFor binary. Binaries should not be included in git :)

@@ -0,0 +1,191 @@
#include "header.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should not be here

@@ -282,6 +282,7 @@ active class Test
end
for v <- arr_passive_join do
if v.tag.eq("pass") then
print("{}\n", v)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

import Boxed.Char
import Boxed.Real
import Boxed.Bool
import Boxed.Unit

active class Main
def main() : unit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the binary Count should not be here

@@ -1857,7 +1876,7 @@ instance Checkable Expr where
(length expectedTypes) (length args)
eArgs <- mapM typecheck args
matchArguments args expectedTypes
return $ setType bottomType abort{args=([]::[Expr])}
return $ setType bottomType abort{args = eArgs} --args=([]::[Expr])} TODO: is this allowed?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the comment to address or to remove?

@@ -2237,7 +2256,7 @@ matchTypes expected ty
bindings <- matchArgs (getTypeParameters expected) (getTypeParameters ty)
`catchError` (\case
TCError (TypeMismatchError _ _) _ ->
tcError $ TypeMismatchError ty expected
tcError $ TypeMismatchError $ trace "2259" $ ty expected
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the trace when you are not debugging anymore

@ElieOaks ElieOaks changed the title For half-way review For-Comprehension Jun 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants