-
Notifications
You must be signed in to change notification settings - Fork 22
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
Find a consistent approach to map Morphir SDK functions to valid Scala #61
Comments
So a few things.
Maybe if we come up with some rules (or laws) we can "by hand" work through what the resulting code would look like. Let's take the above examples: One rule is the OO mapping rule: Description: OO mapping occurs when the final type of a function matches the type of a type alias or type declared within the same module (perhaps we'll have to revisit this restriction later, but lets go with that until we have a counter example). type T = ...
fun : Arg1 -> Arg2 -> ... -> ArgN -> T -> R So this leads to this: class List[A] {
def map[B](f: A => B): List[B]
} When we move on to the map2 : (a -> b -> c) -> List a -> List b -> List c If we look at this there is actually a law consistent with If the first parameter is a genericized lambda then the generated method must be curried and move the first argument to the last to satisfy inference rules. class List[A] {
def map2[A,B,C](listA:List[A])(listB:List[B])(f: (A,B) => C): List[C]
} Now it, just so happens that this same refactoring works for map (which is just a case where we have no additional inputs), and it should hold for While a bit tedious, if we go through Then and only then do we start writing code. |
So This leads to the Constructor/Factory Rule. A function of the form: type TypeA
foo: a -> TypeA Is considered a constructor or factory and as such will be created as a function on the companion object: object TypeA {
def foo[A](arg1:A):TypeA
} |
By the way: My gut feeling has been for a while that we should output both the OO and static version of functions in every case. |
This example doesn't look right: class List[A] {
def map2[A,B,C](listA:List[A])(listB:List[B])(f: (A,B) => C): List[C]
} Since you have both input lists as an argument putting it on the class is confusing. Or did you mean this? class List[A] {
def map2[A,B,C](listB:List[B])(f: (A,B) => C): List[C]
} To me this is a fundamental limitation in OOP (and in every entity based approach, relational and semantic web too). They assume that there is always a single entity/object you are working with which is not the case with mapN functions (and a lot of others). FP (and even structural programming) has an edge here since it doesn't attach functions to entities so it's more generic. I wrote a blog post about the topic of triples not being able to represent logic/data efficiently some time back: https://ozmi.wordpress.com/2015/01/17/a-unified-model-for-data-and-logic/ Anyway, I digressed. Main thing is I think the OO can only apply if there is exactly one argument (should probably limit it to the last) with a type in the same module. For the rest we should simply not apply the OO rule. |
By the way this is how I implemented the OO rule last week: |
You are right, what I wanted to write was: class List[A] {
def map2[A,B,C](listB:List[B])(f: (B,A) => C): List[C]
} Now this looks a bit strange, because the type parameters are renamed. Also I do agree that the functional encoding of this is easier. |
So what's the conclusion here? As I'm reading through this again I'm reminded that all this rearranging will have a significant impact on the call-site especially if the functions are partially applied. It will get very difficult if we change the ordering. Maybe it would be better to just map everything directly as we do now and focus on adding type information where needed? |
There are a number of differences in the semantics of Morphir compared to Scala which makes it challenging to come up with a consistent mapping. Let's start with a simple SDK function of
List.map
:The direct Scala mapping of this would be:
There are multiple things here that are not idiomatic. Here is how the idiomatic version would look like:
There are a number of differences between the approaches:
f
. This makes type-inferencing difficult because it's generally applied left-to-right.We can generalize the mapping approach to the following. Given this Morphir model:
The generated Scala should be:
This will work and look natural in most cases but there are edge cases:
This function doesn't have a trivial method mapping since the type appears multiple times in the argument list. It seems that a better approach here is to use a static method/function but still avoid currying and put the lists into the front:
This is more difficult to formalize.
The text was updated successfully, but these errors were encountered: