-
Notifications
You must be signed in to change notification settings - Fork 3
[PR-1190] Article about typescript for haskellers #5
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
[PR-1190] Article about typescript for haskellers #5
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed about 70% of the text, so far so good, really interesting info.
Left some comments for now.
Also, I think at some point later we will need more reviewers on the technical part because I find some points in the storytelling/examples/explanations controversial (aside from those that are mentioned in the review).
typescript-for-haskellers/article.md
Outdated
let a: A = {}; | ||
const b: B = {x: 1}; | ||
a = b; | ||
console.log(a.x); // Property 'x' does not exist on type 'A'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Property 'x' does not exist on type 'A'.
the outputted text or compile-time error? If it is the latter, I would simplify to const c = a.x
to make it clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:cho:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please provide explanation (hand-wavy at least!) on how all of that black magic works.
Without it, this will scare the haskellers.
typescript-for-haskellers/article.md
Outdated
It doesn't matter how small or complex it is. | ||
From one click apps with pictures converting to photoshop everyone wants fast and easy access to it and Web is where you can give it to your users or even for you with the easiest way. | ||
|
||
Moreover, UI and UX standards are increasing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raising or improving
typescript-for-haskellers/article.md
Outdated
From one click apps with pictures converting to photoshop everyone wants fast and easy access to it and Web is where you can give it to your users or even for you with the easiest way. | ||
|
||
Moreover, UI and UX standards are increasing. | ||
People like simple interfaces with enjoyable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with joy
typescript-for-haskellers/article.md
Outdated
Application with worse functionality and better UI/UX may win on the market against the other one with bad design. | ||
|
||
One might say that doesn't matter what your application is about or what part of it you write. | ||
In the end in most of the cases you will need to give access for it to smb. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the end in most of the cases you will need to give access for it to smb. | |
In the end you'll have to give access for it to somebody other. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest replacing dot with comma and gluing the next sentence.
|
||
type ToS<A> = { | ||
toString: string; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't ToS
be Interpreter<String>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, the idea was to take example from our fp course https://slides.com/fp-ctd/lecture-11#/18/0/1
And as I understand the problem (or it is not a problem) here is that it is an imitation of HKT. Also we can add to URItoKind
anything we want. Also we use just type aliases. So, yes I can write just Interpreter<String>
but I wanted to make it look similar to Haskell.
Also I don't really know what different interpreter to write as we don't use <A>
in ToS
(but we also don't use it in Haskell implementation)
So, if you have any ideas I will change it.
} | ||
} | ||
|
||
const arithInterpreter: ArithExpr<"Interpreter"> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArithExpr<"Interpreter">
What. How does it work? Please explain why are we putting a name of a field here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess, URI is an interface to string-like objects, but what is it here, exactly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I thought that HKT part explain it (do I need to explain in here again?)
URIS
is just keys(properties) of URItoKind
where URItoKind
is like map from keys to types
Here we say that ArithExpr
take Expr extends URIS
and types in ArithExpr
will be Kind<Expr, number>
for example where Kind
is a type from URItoKind
with concrete keys(properties) from URIS
:ghc:
Let's think how to make it more understandable if HTK
part doesn't describe it
|
||
testExpr(arithInterpreter).interpret; // false | ||
testExpr(arithToS).toString; // (((23 + 12) > 170) && (35 > 47)) | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code above looks deceptively similar to haskell implementation, but in reality, there's a lot of magic in need of explanation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the blocks of code, I usually consider them subjectively understandable, if I can sit and make an analogous code myself, given the example and an explanation.
Here, I can't, because there is no textual explanation.
and: (l: Kind<Expr, boolean>, r: Kind<Expr, boolean>) => Kind<Expr, boolean>; | ||
}; | ||
|
||
type Interpreter<A> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading the code block to the end, I start wondering what the A
here really is. Plz explain how it is connected to other spells.
typescript-for-haskellers/article.md
Outdated
This is the very simple examples of working with ADT like structures in TypeScript. | ||
Using type parameters you are also able to make some more complex things like [such](https://github.com/pfgray/ts-adt) library for ADTs. | ||
But one of the most useful features of Haskell connected with ADTs is pattern matching. | ||
In allows you to disclose you structure comparing it with some pattern and based on this condition execute some code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was not able to understand this sentence, sorry. :D
Grammar edits that don't require assistance.
Looks like I added more code descriptions and answered/fixed all comments. It is possible that I missed smth or some parts need more detailed explanations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed the remaining part (starting from the mapped types) ~superficially, looks quite good overall.
Things look really well explained (but not all of them yet, hope that resolving the remaining @Heimdell's comments will improve the situation). I mostly had no questions like "WTF is going on here". Many examples looked sort of familiar, so it was relatively easy to understand them (though will I be able to re-implement something here with the same ease - that would be an interesting question).
But for future reference, I again express my concern regarding the number of covered topics. The article starts from the basic things like syntax and types, then without giving a break it introduces tricky types where some decent understanding of basics is already required. Then the post picks up the pace even more, using concepts that are not explained anywhere, and keeping this pace is now hard for you as a writer.
We will use `fp-ts` to implement it. | ||
|
||
```typescript | ||
import { Kind, URIS } from "fp-ts/HKT"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scope of the post is really large, the difficulty of described concepts grows exponentially throughout the text, so many such issues arise.
But need to say, things that are now explained (like Kind
) looked explained quite well to me and embedded into the text nicely. At least those that are similar to what I have already worked with in Haskell. Other things still look mind-blowing, but that's probably exactly the scope of the post to only provide the transition from known Haskell concepts to TS.
https://haskellfortypescriptdevs.fission.codes/part-i -- Just saw this, perhaps it can serve as partial inspiration. |
You mean in the end of the article (to summary part) or how? First of all when I started to write this article I thought that it will be for people who already knew the TypeScrips syntax. Then in review there was a good point to describe some non-trivial things and I added them. But, hm, I don't really know what to add in this table if it will be in the beginning as the whole article is about translating Haskell code to TS code. I can do it in code parts like creating Haskell examples to all TS examples (but not really know how do it) Anyway I am not against your suggestion, just want to understand how to make it more useful and not just copy paste of the article
I also thought about it. But in my head it looked like the first part is for all, there are no really hard concepts and the second part is for inspiration. I specially wrote that it is just examples with descriptions and for more info you need to go here and here. There is no reason to write a full article about them as only few amount of people will need it and even fewer amount of people will use it. |
Yes, I would put it at the end, or maybe even as a separate gist. This table should answer the question: "I want to declare X thing (e.g. polymorphic datatype) in TypeScript, remind me in 5 seconds which code should I write". On the other hand, the resource that @NaeosPsy mentioned looks quite good, maybe leaving a link in some
I see, told this way it really makes sense, yeah. |
https://serokell.io/blog/parser-combinators-in-haskell You can also take a look at how Heitor handled it in appendix of parser combinator article. |
@Martoon-00 @NaeosPsy I spent full day trying to understand how to make the table useful and informative And after all table has really small amount of space to write and there is no ability to add code blocks |
You can definitely use tables + code blocks. (See Vlad's translations from Haskell to Core, we just did HTML tables: https://serokell.io/blog/haskell-to-core) A TypeScript to Haskell guide would be quite odd in this case. This particular item should serve as an aid for memory, so it also doesn't have to contain explanations of TypeScript stuff. Basically, you can look at it as creating a "cheatsheet" for your article. In case something is too long, you can just use an anchor link to the corresponding section. You can also link to the other one at the bottom of it if you wish. Edit: I've looked at the one you've already created, and it is okay, just that there is no space for the code. If you had only two columns at max, it would be solved. I think the table can be restructured or divided so that it works like that. |
@Martoon-00 @NaeosPsy I made a table, not sure if it will look good on the site. Also I don't know if it contain information you thought about. |
The tables are just awesome! Thanks for adding them 👍 One more thing I'd like to see there: does TS have something like type G<A> = A extends number ? boolean : A extends string ? boolean : blowUp; similar to type family G a where
G Int = Bool
B String = Bool
-- no "otherwise" clause ? |
@Martoon-00 |
Gints, are we gonna follow up on our collab with Brooklyn with this?
…On Fri, 11 Feb 2022, 01:21 Sasha Pakulev, ***@***.***> wrote:
@Martoon-00 <https://github.com/Martoon-00>
type G<A> = A extends number ? boolean : A extends string ? boolean :
never;?
—
Reply to this email directly, view it on GitHub
<#5 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AK2DJQZ755RZ7PZ5PGEXBIDU2RQBZANCNFSM5JCB5FMA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because your review was requested.Message ID:
***@***.***>
|
Ah no, this would correspond to |
|
Ooh right, and this perhaps looks even better than for Haskell. Could you include this? Like, here the correspondence to Haskell is not 1-to-1, would be nice to have this mentioned as a hint. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing my comments!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks overall good.
Some demon-conjuration-spells are not clearly explained, but most of them are. I think readers should start poking into TS just out of initial contact shock.
The appendix with haskell-TS correspondence is nice.
|
||
In the first `if`, we pass `typeof result === "number"`, so we know that it may be only `function` or `string` in the code below. | ||
And we can run `length.toString()` on this value (even if `number` doesn't have such a property) since both `function` and `string` have the property `length`. | ||
But we can't `call` this value because `string` is not callable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But can we call it, if all constituents of the union are callable? Will it infer the result to be union of function result types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, added note
|
||
```typescript | ||
const resultInterpreter = (result: Result): string | undefined => { | ||
if (typeof result === "number") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, if
statement is enriched with case analysis, when is used with typeof result === "number"
.
Will typeof result === "number" || typeof result === "string"
do the trick, leaving type to be function
in else
-block?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, added note
return match(result) | ||
.with({ type: 'error' }, (res) => `<p>Oups! An error occured</p>`) | ||
.with({ type: 'ok', data: { type: 'text' } }, (res) => `<p>${res.data.content}</p>`) | ||
.with({ type: 'ok', data: { type: 'img', src: select() } }, (src) => `<img src=${src} />`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the rule "res
is the matched payload or arguments are the ones chosen with select()
"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like yes, but ts-pattern
have a lot of things, it is a simple example just for design understanding
typescript-for-haskellers/article.md
Outdated
a.x; // 2 | ||
``` | ||
|
||
For this reason, you should always write proper types of arguments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was moved here by mistake
}); | ||
``` | ||
|
||
In the code example above, `produce` is a base primitive for working with `immer`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it remove all readonly
-limitations from type of a
via Writable<>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really know how it works under the hood, but what for it may need Writable
? Search in their repo by Writable
said that there is no Writable
typescript-for-haskellers/article.md
Outdated
|
||
#### Parametric polymorphism | ||
|
||
Parametric polymorphism allows us to write abstract functions or data types that don't depend on their type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parametric polymorphism allows us to write abstract functions or data types that don't depend on their type. | |
Parametric polymorphism allows us to write abstract functions that, for instance, don't depend on concrete types of particular arguments, and types that can abstract out parts of itself as type parameters. |
import { identity } from "fp-ts/lib/function"; | ||
|
||
interface Equality<A, B> { | ||
(a: A): B; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that mean, that Equality
should be a funciton A -> B
?
} | ||
} | ||
|
||
const arithInterpreter: ArithExpr<"Interpreter"> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess, URI is an interface to string-like objects, but what is it here, exactly?
and: (l: ToS<boolean>, r: ToS<boolean>) => { | ||
return { toString: `(${l.toString} && ${r.toString})` }; | ||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My wild guess is that "ToS"
here is a marker or has the same role as
instance IsExpr ToS where
in haskell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand you right the answer is yes
I don't really like how it looks, but I wanted to make it looks like in haskell and didn't want to invent more complex examples as it is already complex enough. So, I thought a lot about it and as it is kind of advanced part I think we can leave it like this
No description provided.