diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..28c6940 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["antifragile", "replayable"] +} diff --git a/docs/docs-intro/index.mdx b/docs/docs-intro/index.mdx index 5e5302b..ad2539e 100644 --- a/docs/docs-intro/index.mdx +++ b/docs/docs-intro/index.mdx @@ -4,58 +4,61 @@ sidebar_position: 1 import Badge from "@site/src/components/Badge"; -# Note on the Docs +# Support NeoHaskell -NeoHaskell is an ongoing effort that is yet in an early development stage. Throughout the documentation you might find badges like the following: +## Building the Future, One Event at a Time -**Try hovering your mouse over it!** (and if you click on it, it will take you to the GitHub issue that's tracking the implementation). +Hi, this is Nick, the creator of NeoHaskell. -This badge means that this feature is **documented but it is not implemented.** This is on purpose. +NeoHaskell exists because we believe software development doesn't have to be painful. That adding your 100th feature should be as easy as your first. That teams should stay small and happy even as systems grow massive. -We believe that the documentation is the central part of any good -development experience, therefore we first write the documentation, and -only then, we implement the required things to make the feature defined -in the documentation possible. +Right now, I'm working on NeoHaskell full-time, living off my savings to make this vision real. Every contribution—whether financial, technical, or spreading the word—directly impacts how fast we can deliver a world where technical debt becomes obsolete. -One of the primary statements of NeoHaskell is: +## Ways to Support -:::tip If it takes more than 15 minutes to figure out, it is a bug -::: +### Financial Support -This doesn't apply only to the command line tool, libraries, and so on, but also **to the documentation itself**. +Choose the platform that works best for you: -## Requesting Clarification +- **[GitHub Sponsors](https://github.com/sponsors/NickSeagull)** - Direct support through GitHub +- **[OpenCollective](https://opencollective.com/neohaskell)** - Transparent collective funding +- **[Ko-fi](https://ko-fi.com/nickseagull)** - One-time or monthly contributions -If you find yourself reading some part in the documentation and you're having trouble understanding it, click on the -**Edit this page** button on the bottom left of the page, and the GitHub repository with that page will open. +Every dollar extends the runway. Every month of runway means more features, better documentation, and a stronger foundation for the companies that will build their future on NeoHaskell. -Click on the top right pencil icon to open the editor. +### Hire Me -![pencil location](img/02-pencil.png) +The best way to support NeoHaskell? **Use it in production.** -In that editor, mark the part that you find unclear. You can use any kind of marker that you want, as long as it marks well the part you're not clear about. For example: +I'm available for consulting to help your team: +- Architect event-driven systems that actually work +- Transition from CRUD to event sourcing without the pain +- Build your next product with NeoHaskell +- Train your team on event modeling and domain-driven design -```text ---->>> some documentation sentence <<<--- rest of documentation.... -``` +When you hire me, you're not just getting consulting—you're funding NeoHaskell's development while solving your own technical challenges. Every production use case makes NeoHaskell stronger. -Once you finished, click on the big green button on the top right, select "Create a new branch for this commit and start a pull request" and click on propose changes: +**[Contact me for consulting](mailto:consulting@nickseagull.dev)** -![propose changes explanation](img/02-propose-changes.png) +### Other Ways to Help -A screen will appear, fill it with the title of your request and some description on what could help you understand this better. +**Use It**: Build something with NeoHaskell. Your feedback shapes what we build next. -![pull request](img/02-pull-request.png) +**Share It**: Tell others who are suffering from exponential complexity. Write about your experience. Show what you've built. -Click on **Create Pull Request**, someone from the contributor team will be notified about this, so the section can be -clarified. +**Contribute**: Code, documentation, examples—every contribution matters. We're building this together. -## This is Important +**Star It**: Give us a star on [GitHub](https://github.com/neohaskell/neohaskell). It helps others discover the project. -This process helps everyone to maintain a great documentation that everyone can understand, and these are actually the -most important contributions, as when you have experience and knowledge about any topic it is very easy to miss on the -actual details that newcomers can find daunting. +## Why This Matters -![documentation beats conversation](img/docs-beat-convo.gif) +Your support isn't just funding a programming language. You're investing in a future where: -Thanks for reading this part! Now onwards to learn NeoHaskell! +- Startups don't fail from technical bankruptcy +- Developers don't burn out from complexity +- Companies scale without suffering +- Software development becomes humane again + +Together, we're proving that software can get better, not worse, over time. + +**Thank you for believing in this vision.** diff --git a/docs/essentials/_category_.json b/docs/essentials/_category_.json deleted file mode 100644 index 337ecf1..0000000 --- a/docs/essentials/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Essentials", - "position": 2, - "collapsed": false -} diff --git a/docs/essentials/constants.mdx b/docs/essentials/constants.mdx deleted file mode 100644 index d5d7c3d..0000000 --- a/docs/essentials/constants.mdx +++ /dev/null @@ -1,91 +0,0 @@ ---- -sidebar_position: 1 ---- - -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# Constants - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -NeoHaskell promotes the usage of _variables that do not change over time_. These kinds of variables are named -**constants**. - -In most of programming languages, what you expect is to use regular variables that can change, and then only -if you think that some variable should not change, you create a constant. In NeoHaskell, the approach is the -opposite: you should use constants by default, and only if you think that some variable should change, you -create a **mutable variable** (we will talk about mutable variables in later sections). - -In software development, mutable variables introduce cognitive overhead because **developers must track changes to their values throughout the code**, which can lead to unpredictable side effects and bugs. On the other hand, constants offer clarity and predictability. When you see a constant, **you instantly know its value will remain consistent everywhere**, eliminating the mental effort of tracking potential changes and simplifying the code's comprehension. - -## Declaring Constants - -Declaring constants is easy, just write the name of the constant, followed by an equals sign and the value of the constant: - -```haskell -neo> myConstant = 10 -``` - -The above code declares a constant named `myConstant` with the value `10`. You can now use it to perform operations: - -```haskell -neo> myConstant + 5 -15 -``` - -## A Note on Naming Convention - -Note how the constant name is written with the first letter in lowercase, and then for each word, the first letter is written in uppercase. This is called **camelCase** and is the naming convention in NeoHaskell for **constants**, **variables** and **functions**. - -Another thing that might have caught your attention is that it is not written in all caps, like `MY_CONSTANT`. This is because in NeoHaskell, everything is a constant, and variables have special syntax to declare them as mutable. So, there is no need to write constants in all caps. - -If you try to define a constant in all caps, you will get an error message: - -```text -neo> MY_CONSTANT = 10 -error: - It looks like you're trying to define a constant in all caps. In NeoHaskell, constants are defined in `camelCase`, - as names that start with uppercase letters are reserved for types. - - Read more about constants in the docs: https://neohaskell.io/docs/essentials/constants -``` - -## They Are Really Constant, Tho - -You might be wondering, what happens if I try to change the value of a constant? Let's try it: - -```haskell -neo> myConstant = 10 -neo> myConstant -10 -neo> myConstant = 20 -neo> myConstant -20 -``` - -> Hey, you lied to me! You said that constants are constant, but I just changed the value of `myConstant`! - -Well, not really. What you did was to create a new constant named `myConstant` with the value `20`. The old constant named `myConstant` with the value `10` doesn't exist anymore. - -Let's try doing one thing, incrementing the constant by one: - -```haskell -neo> myConstant = 10 -neo> myConstant = myConstant + 1 -neo> myConstant - --- oops! the REPL froze! 🥶 -``` - -Nope, it's not your computer, the REPL just froze (you can press `Ctrl + C` to cancel the operation), and it is an expected behavior. - -Why is this happening? What's happening here is that when defining a constant, the compiler is not really calculating the -value on the right side of the equals sign, but instead, it is **just storing the expression as it is**. - -When you try to use it (like when you write `myConstant` in the REPL), the compiler will calculate and store the value of the -constant at that moment, as it is the first time that you are using it. This is called **lazy evaluation**, and we will go more -in depth in the next section. diff --git a/docs/essentials/enums.mdx b/docs/essentials/enums.mdx deleted file mode 100644 index 7c15680..0000000 --- a/docs/essentials/enums.mdx +++ /dev/null @@ -1,238 +0,0 @@ ---- -sidebar_position: 5 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# Enums - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -In the previous section, we learned how to start using NeoHaskell's type system, we learned how to annotate constants -and functions, and also we saw how to start modelling our domain using wrapper types, which allowed us to -avoid primitive obssession and to give concrete more meaningful names to our types, more according to the domain of our -application. - -There are certain cases where we might want to model a thing that can only have a finite number of values, for example, -the state of a lightbulb, the colors of a rainbow, the days of the week, etc. Instead of using strings to represent these values, we use **enums**. - -Similar to wrapper types, enums allow you to avoid primitive obsession. For example, instead of using a `Bool` to represent -the state of a lightbulb, we can use an enum with two cases: `On` and `Off`. This way, we can't accidentally pass a `Bool` -that represents the state of a lightbulb to a function that expects a `Bool` that represents the state of a TV. - -## Defining an enum - -To define an enum, we use the `data` keyword followed by the name of the enum and the cases that it can have, separated by -vertical bars (`|`). For example, to define an enum that represents the state of a lightbulb, we can do the following: - - - - -```haskell -data LightbulbState - = On - | Off -``` - - - - -```typescript -enum LightbulbState { - On, - Off, -} -``` - - - - -You can add as many cases as you want. In fact, if your application handles some specific values of a type with infinite values like -`Int`, it is much better to use an enum instead of an `Int` because it will be much more clear what the function expects and -what it returns. - -Imagine that you're developing a videogame, and you want to represent with how many lives does a player start. Let's suppose that a -player can only start with between two and six lives. Instead of using `Int`, which has no limits, we can use an enum with different -cases. This way, we can't accidentally pass a `1` to a function that expects the number of starting lives of a -player, or have a bug down there in our code that makes the player accidentally start with one million lives: - - - - -```haskell -data PlayerLives - = Two - | Three - | Four - | Five - | Six -``` - - - - -```typescript -enum PlayerLives { - Two, - Three, - Four, - Five, - Six, -} -``` - - - - -One advantage of NeoHaskell's enums over TypeScript's (and most mainstream languages) enums is that the values of a NeoHaskell enum -are the values themselves and they are not usable in place of other types that are not the enum itself, while in TypeScript, -for example, the values of an enum are numbers that represent the position of the case in the enum, and they can be used in any place -where a number is expected. Compare the following examples: - - - - -```haskell -data PlayerLives - = Two - | Three - | Four - | Five - | Six - -neo> Two + 4 --- TYPE MISMATCH ---------------------------- -Attempting to add two values, but the first value doesn't match the type of the second value: - - Two + 4 - ^^^ - -`Two` is of type: - - PlayerLives - -But `(+)` needs the 1st argument to be: - - Int -``` - - - - -```typescript -enum PlayerLives { - Two, - Three, - Four, - Five, - Six, -} - -console.log(PlayerLives.Two + 4); // 4 - -// The above weirdly prints 4 because the value of `PlayerLives.Two` is 0, and 0 + 4 = 4 -``` - - - - -It definitely is much better to have this fail at compile time than to have a weird bug where there's a value called -`Two` that is actually `0` and that can be used in place of a number. - -## Enums with Attached Values - -In addition to simple enums, NeoHaskell allows the creation of more complex enums by attaching values to enum cases. These are powerful constructs that enable enums to carry additional, context-specific data. This feature is particularly useful when a simple label (like `On` or `Off`) isn't sufficient to express all the necessary information about an enumeration case. - -### Defining Enums with Attached Values - -When defining an enum in NeoHaskell, you can specify one or more values to be attached to each case. These values can be of any type, including other enums or complex types. This makes enums a flexible tool for modeling a wide variety of data. - -Consider a `Color` enum where a color can be represented in different color spaces. Some colors might be represented in the RGB color space, others in grayscale, and yet others as hexadecimal strings. Here's how you can define such an enum: - - - - -```haskell -data Color - = Rgb Int Int Int -- Red, Green, Blue components - | Grayscale Int -- Intensity of gray - | Hex String -- Hexadecimal string - --- To create a value of type `Color`, you use the following syntax: -myFavoriteColor = Rgb 0 255 0 -``` - - - - - -```typescript -// In Typescript there's no way to attach values to enum cases, so we have to use a workaround - -// First we define a type that represents the different color types -enum ColorType { - Rgb = "Rgb", - Grayscale = "Grayscale", - Hex = "Hex", -} - -// Then we define a type that represents a color -type Color = - | { type: ColorType.Rgb; r: number; g: number; b: number } - | { type: ColorType.Grayscale; intensity: number } - | { type: ColorType.Hex; hexStr: string }; - -// And now we define a couple of functions to create colors -const Rgb = (r: number, g: number, b: number): Color => ({ - type: ColorType.Rgb, - r, - g, - b, -}); - -const Grayscale = (intensity: number): Color => ({ - type: ColorType.Grayscale, - intensity, -}); - -const Hex = (hexStr: string): Color => ({ - type: ColorType.Hex, - hexStr, -}); - -// To create a value of type `Color`, we use the functions that we defined above -const myFavoriteColor = Rgb(0, 255, 0); -``` - - - - -Each case of the `Color` enum carries different types of values: - -- `RGB` carries three `Int` values representing the red, green, and blue components of a color. -- `Grayscale` carries a single `Int` representing the intensity of gray. -- `Hex` carries a `String` representing the color in hexadecimal format. - -This design encapsulates the concept that a color can be represented in different ways, but ultimately, it's still a color within the domain of your application. - -### Benefits of Enums with Attached Values - -Using enums with attached values has several benefits: - -- **Richer Data Modeling**: You can model complex data structures in a type-safe manner, ensuring that the attached data aligns with the specific case of the enum. -- **Clarity**: The code clearly communicates what data is expected with each enum case, leading to self-documenting code. -- **Safety**: The compiler can enforce that all possible cases are handled in functions, reducing the likelihood of runtime errors. A function that doesn't handle all possible cases for an enum will fail to compile. - -In summary, enums with attached values in NeoHaskell are a powerful feature for developers to express complex data structures cleanly and safely. They extend the utility of simple enums by allowing the carrying of additional information, which can be crucial for many applications that require detailed data representation and manipulation. And given the event-driven nature of NeoHaskell, enums with attached values are a natural fit for modeling events and their associated data. - -## Next Steps - -We've learned how to define enums, and how to attach values to enum cases, but we haven't seen how to use them yet. -In the next section, we'll start learning about handling boolean conditions through the usage of `if-then-else`, -and later, we will transition towards pattern matching, which will help you to use your enums. diff --git a/docs/essentials/functions.mdx b/docs/essentials/functions.mdx deleted file mode 100644 index bedff32..0000000 --- a/docs/essentials/functions.mdx +++ /dev/null @@ -1,160 +0,0 @@ ---- -sidebar_position: 3 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# Functions - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -NeoHaskell promotes the style of programming that's called **functional programming**. There are so many definitions of -functional programming, but it boils down to one thing: **programming with functions**. - -Functions can be divided in two types: **Calculations** and **Actions**. - -A **calculation** is a function that will always return the same result for the same input. **In NeoHaskell, they cannot crash/throw an error**. Examples of calculations are: - -- Performing mathematical operations (adding, subtracting, etc) -- Returning the length of a list -- Converting one object to another -- Doing operations with strings - -An **action** is a function that usually relies on some external thing to perform its job. **These can throw errors.** Examples of actions are: - -- Reading a file from the disk -- Sending an email -- Modifying some data in the state of the application -- Printing something to the screen - -This distinction is crucial, because when looking for a crash in your application, you can be sure that the problem is -in an action. - -**In NeoHaskell, by default all functions are calculations**. The compiler won't let you perform an action without explicitly -telling it that you're doing so. We will cover actions in later sections, so for now, just know **that all functions you see are calculations, and cannot fail/crash.** - -## Calling a Function - -In NeoHaskell, to call a function, you **write its name and then the arguments separated by spaces**. This usually baffles people coming from other languages, but it helps you write more readable and beautiful code. - -Suppose we want to call a function called `estimateShipping` that returns the cost based on weight in kilos, distance in kilometers, and cost per kilometer. Here's how you would do it. Compare the NeoHaskell code, and the JavaScript code (click on the tabs to switch languages): - - - - -```haskell -estimateShipping 15 100 2 -``` - - - - - -```python -estimateShipping(15, 100, 2) -``` - - - - -The only time that we use parentheses is when we want to pack another function call or using an operator as an argument. For example, if we wanted to call `estimateShipping` adding the weights as the first argument, and with the result of `calculateDistance` as the second argument, we would do it like this: - - - - -```haskell -estimateShipping (7 + 8) (calculateDistance 10 20) 2 -``` - - - - - -```python -estimateShipping(7 + 8, calculateDistance(10, 20), 2) -``` - - - - -At this point, it is definitely not clear that one style is more beautiful than another. But it is because that also it is -advised to extract the arguments to constants, and then call the function with the constants, inside of a function that you've -defined. - -## Defining Functions - -To define a function in NeoHaskell, you write the name of the function, followed by the arguments, and then an equal sign, and then a block for the body of the function. This is too much information to process in a sentence, so let's see an example. - -Let's write a function that calculates the shipping cost, given the weight, distance, and cost per kilometer: - - - - -```haskell -estimateShipping weight distance costPerKm = do - let distanceCost = distance * costPerKm - let weightCost = weight * 2 - distanceCost + weightCost -``` - - - - - -```js -function estimateShipping(weight, distance, costPerKm) { - const distanceCost = distance * costPerKm; - const weightCost = weight * 2; - return distanceCost + weightCost; -} -``` - - - - -In the example above, we're using a **block** to define a function, so it allows us to define constants with the `let` keyword. Note how that the last line in a block is -returned, so there's no need to write `return` like in -other languages. - -**Blocks are optional**, and if we wanted to write the same function without it, removing all the constants, -and writing the calculation inline, we could do it like this: - - - - -```haskell -estimateShipping weight distance costPerKm = - distance * costPerKm + weight * 2 -``` - - - - - -```js -function estimateShipping(weight, distance, costPerKm) { - return distance * costPerKm + weight * 2; -} -``` - - - - -This way of writing code helps writing data processing code and validation rules in a very readable way. **But be careful**, -because one-liners can easily become hard to read, so **it is advised to use blocks for more complex functions.** - -## A Note on Cognitive Overhead - -In the function we've defined above, we're naming our arguments `weight`, and `distance`. Is the weight in kilograms or pounds? Is the distance in kilometers, miles, [or alligators](https://edition.cnn.com/2020/04/04/us/social-distancing-florida-alligator-trnd/index.html)? We don't know, and we might have to look at the implementation of the function to know. And still, we'd have no clue, because there aren't even comments in the function. - -This is extra work that our brains must have to do, and when you're trying to navigate a codebase with hundreds (or thousands) of functions, it can become a nightmare. These kinds of issues might seem anecdotic, but actually [the NASA lost millions due to a similar situation](https://www.simscale.com/blog/nasa-mars-climate-orbiter-metric/). - -NeoHaskell takes an approach that helps you define explicit contracts for all of the functions. We do so by **modeling our domain** (the problem we're trying to solve) **with types**. - -Let's hop into the next section to learn more about them! diff --git a/docs/essentials/if-then-else.mdx b/docs/essentials/if-then-else.mdx deleted file mode 100644 index 7c6ac41..0000000 --- a/docs/essentials/if-then-else.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -sidebar_position: 6 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# If-Then-Else - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -After exploring the robust type system of NeoHaskell and learning about enums, let's turn our attention to making decisions in our code with conditional expressions. The `if-then-else` expression is a fundamental construct that you, as a TypeScript developer, might recognize as similar to `if...else`. It's the bread and butter of decision-making in programming. - -## The Basics of `if-then-else` - -In NeoHaskell, `if-then-else` allows your program to execute different expressions based on a boolean condition. The structure is simple: - -```haskell -if then - -else - -``` - -- ``: A boolean expression evaluated by the program. -- ``: The result when the condition is `True`. -- ``: The result when the condition is `False`. - -:::tip -Remember, the `if-then-else` construct in NeoHaskell is an expression, not a statement. This means it always results in a value, making your code more predictable and easier to understand. -::: - -## Example in Action - -Consider a function that determines if a number is positive, negative, or zero. Here's how it might look: - - - - -```haskell --- We create a simple enum for the example -data Sign - = Positive - | Negative - | Zero - --- Gets the sign of a number -getSign :: Int -> Sign -getSign n = - if n > 0 then - Positive - -- To handle multiple conditions, we can use `else if` - else if n < 0 then - Negative - else - Zero -``` - - - - -```typescript -// We create a simple enum for the example -enum Sign { - Positive, - Negative, - Zero, -} - -// Note how the NeoHaskell `if` is equivalent to a ternary, -// not a TypeScript `if` statement -function getSign(n: number): Sign { - return n > 0 ? Sign.Positive : n < 0 ? Sign.Negative : Sign.Zero; -} -``` - - - - -## Key Points to Remember - -- **Complete Expressions**: Every `if` must be accompanied by a `then` and an `else` in NeoHaskell to ensure the expression is complete. -- **Indentation is Key**: Proper indentation is crucial in NeoHaskell; it delineates the branches of your `if-then-else` expressions clearly. -- **No Parentheses Needed**: Unlike TypeScript, you don't need to wrap the condition in parentheses. - -:::note -In NeoHaskell, every expression must return a value, which is why an `else` clause is mandatory. This guarantees that your function always has a value to return, no matter what the condition evaluates to. -::: - -## Wrapping Up and Looking Forward - -With the understanding of `if-then-else`, you're now equipped to handle basic conditional logic in your NeoHaskell programs. It's a stepping stone towards more complex decision-making structures like pattern matching, which we will explore soon. In the next section, we'll delve into the powerful world of pattern matching, where you'll learn to handle various cases of enums and other data types elegantly. diff --git a/docs/essentials/img/Bitwise-operator-left-shift.png b/docs/essentials/img/Bitwise-operator-left-shift.png deleted file mode 100644 index ebd3724..0000000 Binary files a/docs/essentials/img/Bitwise-operator-left-shift.png and /dev/null differ diff --git a/docs/essentials/img/Bitwise-operator-right-shift.png b/docs/essentials/img/Bitwise-operator-right-shift.png deleted file mode 100644 index 7d3125a..0000000 Binary files a/docs/essentials/img/Bitwise-operator-right-shift.png and /dev/null differ diff --git a/docs/essentials/img/types_lie.jpeg b/docs/essentials/img/types_lie.jpeg deleted file mode 100644 index 8e9aa62..0000000 Binary files a/docs/essentials/img/types_lie.jpeg and /dev/null differ diff --git a/docs/essentials/laziness.mdx b/docs/essentials/laziness.mdx deleted file mode 100644 index 2e8e3d0..0000000 --- a/docs/essentials/laziness.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -sidebar_position: 2 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# Laziness - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -NeoHaskell is a lazy language. Not only because it gives you a lot of tools so you can focus on what matters in your app, -but also because it is **lazily evaluated**. - -Lazy evaluation is especially beneficial because it can improve application load time, and it can also improve performance -in some cases. - -## What is lazy evaluation? - -Normally, in programming languages like JavaScript, when we create a variable and assign it a value, the value is calculated -immediately. We say that this value has been evaluated **eagerly**: - - - - -```javascript -const myConstant = 1 + 1; - -console.log(myConstant); // 2 -``` - - - - -When we load that JavaScript code, the first that would happen is that the value of `myConstant` would be calculated and stored -in memory. - -Then, whenever we wanted to use the value (like in the `console.log`), we would just retrieve the value from memory. - -Imagine that instead of `1 + 1`, we had a very complex expression that took a long time to calculate. If we were to load that -JavaScript code, we would have to wait for the expression to be calculated before we could use the value. - - - - -```javascript -const myConstant = get500thDigitOfPi(); - -console.log(myConstant); -``` - - - - -This JavaScript code would take a lot of time to load, because it would have to wait for `get500thDigitOfPi` to finish. - -A technique that is usually used in these cases is to wrap the calculation in a function, so that the calculation is only -performed when the function is called: - - - - -```javascript -const myConstant = () => get500thDigitOfPi(); - -console.log(myConstant); // [Function] -``` - - - - -Now we have a problem: we can't use the value of `myConstant` directly, because it is a function. We have to call the function -first: - - - - -```javascript -const myConstant = () => get500thDigitOfPi(); - -console.log(myConstant()); // 2 (after a long time) - -// If we call this again, the calculation gets performed twice. -console.log(myConstant()); // 2 (after a long time, again) -``` - - - - -Now we have another problem, which is that each time that we call `myConstant`, the function `get500thDigitOfPi` is called again, -taking a long time to calculate the value, which doesn't change. So in languages like JavaScript, we would need to implement some -kind of method to store the value of `myConstant` after it has been calculated, so that we don't have to calculate it again. - -## Laziness in NeoHaskell - -In NeoHaskell, by default all values are lazy. As we saw in the previous section, this means that the value of a variable is not -calculated until it is needed. - -The cool part of having this as a first-class feature of the language is that we don't have to be thinking about wrapping the -value in a function, calling the function, or storing the value after it has been calculated. The compiler will do that for us automatically. - -When you define a constant in NeoHaskell, it is lazy by default: - -```haskell -neo> myConstant = 1 + 1 -``` - -It doesn't matter if the calculation is `1 + 1` or `get500thDigitOfPi`, the creation of the constant is instant, because it is -not calculated. - -## Understanding the Freeze from the Constants Page - -In the previous page, we tried to increment a constant by one, but it froze. - -The reason for this, is that due to lazy evaluation, the compiler allows us to define a constant in terms of itself, but -when it tries to calculate it freezes. - -The compiler tries to calculate the value like this (imagine that the comments are being said by the compiler): - -```haskell -neo> myConstant = myConstant + 1 --- I received `myConstant = myConstant + 1`! --- I'm storing the expression `myConstant + 1` as the value of `myConstant`! - -neo> myConstant --- Oh, I need to calculate the value of `myConstant`, in order to print it! - --- Let me check the value of `myConstant`... --- Ok, it is the expression `myConstant + 1`! Let me calculate that... --- First, I need to get the value of `myConstant`... - --- Let me check the value of `myConstant`... --- Ok, it is the expression `myConstant + 1`! Let me calculate that... --- First, I need to get the value of `myConstant`... - --- Let me check the value of `myConstant`... --- Ok, it is the expression `myConstant + 1`! Let me calculate that... --- First, I need to get the value of `myConstant`... - --- Let me check the value of `myConstant`... --- Ok, it is the expression `myConstant + 1`! Let me calculate that... --- First, I need to get the value of `myConstant`... -``` - -And so on. The compiler will keep trying to calculate the value of `myConstant` forever, because it is defined as `myConstant = myConstant + 1`, and it will never be able to calculate the value of `myConstant`. - -This is a suprirising behavior for newcomers, but it is actually a very useful feature that allows the compiler to perform many -optimizations to make your code more performant. - -**For example**, imagine that you have a single-page application that defines two completely different pages for registering a -user: In one, the registered user is a person, in another the registered user is a company. - -A naive implementation in JavaScript would be to have a single `registerUser` function that receives a `userType` parameter, -and depending on the value of that parameter, it would perform a different action: - - - - -```javascript -const registerPersonPage = // ... -const registerCompanyPage = // ... - -if (userType === "person") { - return registerPersonPage; -} else { - return registerCompanyPage; -} -``` - - - - -You see the issue here, both pages are being loaded when the code is loaded, even though only one of them will be used. - -In contrast, in NeoHaskell, the pages wouldn't be loaded until they are needed, because their values would be lazily evaluated. diff --git a/docs/essentials/math.mdx b/docs/essentials/math.mdx deleted file mode 100644 index f1cc3e0..0000000 --- a/docs/essentials/math.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -sidebar_position: 0 ---- - -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# Doing Math - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -As the Professor Walter White said once, "Jesse, let's cook some math", or something like that, I don't know. - -Anyways, let's get to the point. - -## Basic Math - -Here's a table of the typical math operators in NeoHaskell and what they do: - -| Operator | Description | -| -------- | -------------------------------- | -| `+` | Adds two numbers | -| `-` | Subtracts two numbers | -| `*` | Multiplies two numbers | -| `/` | Divides two numbers | -| `**` | Raises a number to a power | -| `%` | Gets the remainder of a division | -| `>>` | Shift right binary operation | -| `<<` | Shift left binary operation | - -## Trying it in the REPL - -### Addition Example - -Lets say that I want to add 56 and 72 and find its result, I can do it as shown: - -```haskell -neo> 56+72 -128 -``` - -### Subtraction Example - -In this example I am subtracting 64 from 112 - -```haskell -neo> 112-64 -48 -``` - -### Division Example - -Lets say I want to divide 117 by 12 and find the quotient, I can do it in NeoHaskell like this: - -```haskell -neo> 117/12 -9 -``` - -### Power Example - -Lets say I want to find what we will get by cubing five (five raised to the power of three), I can do it in NeoHaskell as shown: - -```haskell -neo> 5**3 -125 -``` - -### Remainder Example - -I want to know what we will get as remainder when we divide -21 by 4, I can do it as shown: - -```haskell -neo> -21%4 --1 -``` - -### Right Shift Example - -This operator shifts the bits (the 1's and 0's of the computer representation) of a number to the right by a specified number of bits. For example, if we shift the bits of 5 to the right by 2 bits, we will get 1. This is because 5 in binary is 101, and when we shift it to the right by 2 bits, we get 1, which is 001 in binary. - -
- -```haskell -neo> 5>>2 -1 -``` - -### Left Shift Example - -This operator shifts the bits (the 1's and 0's of the computer representation) of a number to the left by a specified number of bits. For example, if we shift the bits of 5 to the left by 2 bits, we will get 20. This is because 5 in binary is 101, and when we shift it to the left by 2 bits, we get 20, which is 10100 in binary. - -
- -```haskell -neo> 5<<2 -20 -``` - -### Spacing doesn't matter - -NeoHaskell doesn't care about spacing, you can write the above examples as shown below and it will still work: - -```haskell -neo> 56 + 72 -128 - -neo> 112 - 64 -48 - -neo> 117 / 12 -9 - -neo> 5 **3 -125 -``` - -## More Advanced Math - -More advanced math functions are available in the `Math` module. It is -available by default, so you don't need to import it. - -> Modules? Import? What?? - -Don't worry, we'll get to that later. For now, just know that you can use -do some math operations like this: - -```haskell -neo> Math.sin 90 -0.8939966636005579 - -neo> Math.sqrt 64 -8 - -neo> Math.log 10 -2.302585092994046 -``` - -You can experiment and explore the advanced math functions available by -typing `Math.` and then pressing `TAB` to see the list of functions. diff --git a/docs/essentials/pattern-matching.mdx b/docs/essentials/pattern-matching.mdx deleted file mode 100644 index c817b01..0000000 --- a/docs/essentials/pattern-matching.mdx +++ /dev/null @@ -1,188 +0,0 @@ ---- -sidebar_position: 7 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# Pattern Matching - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -Now that you've got a handle on conditional expressions with `if-then-else`, let's elevate your code with NeoHaskell's pattern matching. Unlike the `if-then-else` which branches based on boolean conditions, pattern matching allows you to decompose and examine data directly, making your functions more intuitive and declarative. - -## Understanding Pattern Matching - -Pattern matching in NeoHaskell is akin to a more powerful switch-case statement you might know from TypeScript, but with supercharged capabilities. You can match patterns against any value, as well as data types, such as enums and custom types, and execute code based on the structure of the data itself. - -## Pattern Matching with Integers - -Pattern matching isn't just for complex types. Even with integers, a fundamental type, pattern matching can streamline your code. Let's explore how NeoHaskell handles this with a straightforward example. - -Suppose you want to execute different code based on whether an integer is 0, 1, or any other number. Here's how you would use pattern matching for that: - - - - -```haskell -describeNumber :: Int -> String -describeNumber n = - case n of - 0 -> - "Zero, the absence of quantity." - - 1 -> - "One, the first natural number." - - _ -> - "Some other number." -``` - - - - -```typescript -function describeNumber(n: number): string { - switch (n) { - case 0: - return "Zero, the absence of quantity."; - case 1: - return "One, the first natural number."; - default: - return "Some other number."; - } -} -``` - - - - -In the NeoHaskell snippet, `describeNumber` is a function that takes an integer and uses a `case..of` expression to match it against the patterns `0`, `1`, and `_`, which is a wildcard that matches any number not previously matched. - -## Advantages of Pattern Matching - -- **Readability**: It makes the different cases you're checking against explicit, improving readability. -- **Refactor Safety**: The compiler will warn you if a new case is added to a data type but not handled in your pattern matches. - -Remember, the power of pattern matching in functional programming is that it lets you work with the shape of your data, rather than just the values. Even with simple types like integers, it can make your code more expressive and intent-driven. - -## Pattern Matching with Enums - -Let's put this into practice by matching against the `LightbulbState` enum we previously defined in the -[Enums](/docs/essentials/enums) section. Here's how you would use pattern matching to describe the state of a lightbulb: - - - - -```haskell -describeLightbulb :: LightbulbState -> String -describeLightbulb state = - case state of - On -> - "The lightbulb is shining bright." - - Off -> - "It's dark; the lightbulb is off." -``` - - - - -```typescript -// TypeScript doesn't support pattern matching natively, -// so we have to use a switch-case or if-else instead. -function describeLightbulb(state: LightbulbState): string { - switch (state) { - case LightbulbState.On: - return "The lightbulb is shining bright."; - case LightbulbState.Off: - return "It's dark; the lightbulb is off."; - } -} -``` - - - - -## Dealing with Complex Patterns - -Pattern matching truly shines when you're dealing with complex data types that have attached values. Let's see how you can match and extract these values in a pattern. We'll use the `Color` type we defined in the [Enums](/docs/essentials/enums) section. - - - - -```haskell -describeColor :: Color -> String -describeColor color = - case color of - -- This case only matches if it is an RGB with all 0s. - Rgb 0 0 0 -> - "This is actually black." - - -- This case only matches if it is an RGB with all 255s. - Rgb 255 255 255 -> - "This is actually white." - - -- This case matches any RGB with any values. - Rgb r g b -> - "A colorful RGB with red #{r}, green #{g}, and blue #{b}" - - Grayscale intensity -> - "A grayscale color with intensity #{intensity}" - - Hex code -> - "A hex color #{code}" -``` - - - - -```typescript -function describeColor(color: Color): string { - switch (color.type) { - // Note how in TypeScript, it is not possible to match - // against the values of the attached colors, instead - // we have to match against the type and then extract - // the values from the color object. - case ColorType.Rgb: - // We have to use an `if` statement to check the values - if (color.r === 0 && color.g === 0 && color.b === 0) - return "This is actually black."; - - // Same here - if (color.r === 255 && color.g === 255 && color.b === 255) - return "This is actually white."; - - return `A colorful RGB with red ${color.r}, green ${color.g}, and blue ${color.b}`; - - case ColorType.Grayscale: - return `A grayscale color with intensity ${color.intensity}`; - - case ColorType.Hex: - return `A hex color ${color.hexStr}`; - } -} -``` - - - - -## Best Practices and Pitfalls - -- **Exhaustiveness**: Always cover all cases in your pattern matches. NeoHaskell will fail to compile your code if any are missing, helping prevent runtime errors. -- **Wildcards**: Use the wildcard `_` pattern to catch all other cases that you haven't explicitly handled, although use it judiciously to not mask missing cases that should be handled explicitly. - -:::warning -Avoid overusing the wildcard pattern as it can hide potential match cases that should be explicitly handled, leading to unexpected behaviors. - -As a best practice, it is always recommended to delete the wildcard pattern and handle all cases explicitly. -::: - -## Conclusion and Next Steps - -With pattern matching, you can write more expressive and safer code. It's a cornerstone of functional programming in NeoHaskell, allowing for clear and concise data manipulation. As you grow more comfortable with pattern matching, you'll begin to see its power in simplifying complex data operations. diff --git a/docs/essentials/strings.mdx b/docs/essentials/strings.mdx deleted file mode 100644 index d0a1f81..0000000 --- a/docs/essentials/strings.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -sidebar_position: 8 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# Strings - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -NeoHaskell embraces strings as a versatile and indispensable part of programming, especially when dealing with data that is inherently variable and not known until runtime. - -## Crafting and Utilizing Strings - -Defining a string in NeoHaskell is as simple as in any language—just a matter of enclosing your text within double quotes: - - - - -```haskell -greeting :: String -greeting = "Hello, NeoHaskell!" -``` - - - - -```typescript -let greeting: string = "Hello, TypeScript!"; -``` - - - - -## Purposeful Use of Strings - -While strings are powerful, they should be used judiciously. In NeoHaskell, like in any language that supports strong typing, strings are ideal for representing text values that are inherently unpredictable or user-defined, such as names, email addresses, or free-form text input. - - - - -```haskell -userName :: String -userName = "Jesse123" - -userEmail :: String -userEmail = "jesse@example.com" -``` - - - - -```typescript -let userName: string = "Jesse123"; -let userEmail: string = "jesse@example.com"; -``` - - - - -## Enums vs. Strings - -For values that are known and finite, enums are a more type-safe option than strings. Using enums can prevent errors like typos at compile time rather than at runtime, making your code more reliable and easier to maintain. - - - - -```haskell -data UserRole - = Admin - | User - | Guest - --- Correct use of enums for known, finite values -assignRole :: String -> UserRole -assignRole roleStr = - case roleStr of - "admin" -> - Admin - - "user" -> - User - - _ -> - Guest -``` - - - - -```typescript -enum UserRole { - Admin, - User, - Guest, -} - -// Using enums in TypeScript to represent predefined roles -function assignRole(roleStr: string): UserRole { - switch (roleStr) { - case "admin": - return UserRole.Admin; - case "user": - return UserRole.User; - default: - return UserRole.Guest; - } -} -``` - - - - -## Embracing Type Safety - -NeoHaskell encourages embracing type safety by using strings only when necessary and opting for enums or other more specific types when possible. This practice aids in avoiding common pitfalls associated with string manipulation, such as unexpected mutations or case sensitivity issues. - -## String Operations - -NeoHaskell provides a comprehensive set of functions for string manipulation, allowing you to perform common operations such as concatenation, case conversion, and more, without the overhead of object-oriented methods. - - - - -```haskell -import String - --- Joining strings together -combinedString :: String -combinedString = String.concat ["Neo", "Haskell"] - --- Changing to uppercase -uppercaseString :: String -uppercaseString = String.toUpper "NeoHaskell" - --- Replacing text within a string -replacedText :: String -replacedText str = String.replace "Old" "Neo" "OldHaskell" -``` - - - - -```typescript -// Joining strings together -let combinedString = ["Type", "Script"].join(""); - -// Changing to uppercase -let uppercaseString = "TypeScript".toUpperCase(); - -// Replacing text within a string -let replacedText = "JavaScript".replace("Java", "Type"); -``` - - - - -## Interacting with Strings - -In NeoHaskell, strings are not merely static entities but dynamic constructs that can be inspected, dissected, and transformed, providing robust capabilities for developers to handle text data. - -## Conclusion and Next Steps - -Strings are a fundamental tool in your NeoHaskell toolkit, to be used when the situation demands flexibility and dynamism. As you grow in your NeoHaskell journey, understanding when to use strings and when to opt for more rigid types like enums will be key to writing clean, effective code. diff --git a/docs/essentials/types.mdx b/docs/essentials/types.mdx deleted file mode 100644 index 491fd19..0000000 --- a/docs/essentials/types.mdx +++ /dev/null @@ -1,407 +0,0 @@ ---- -sidebar_position: 4 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; -import Figure from "@site/src/components/Figure"; - -# Meeting the Types - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -One of the key features of NeoHaskell is its type system. Some people have preconceived -ideas about types, and they think that they get in the way of development process too -much. This is usually because either the type system of the programming language they -are using is not flexible enough, or that types come as an afterthought of the -development process of their system, like if it is a "necessary bad thing". - -In NeoHaskell, types become your best friends. Not only because NeoHaskell's type -system is quite different to type systems from other programming languages (e.g. -it doesn't support inheritance, but supports _super-generics_) but also because -it becomes a very useful design tool for your software development toolbox. - -With the NeoHaskell type system, you can sketch an outline of your system, that then -you can fill with the colors of an implementation. If your sketch outlines a dog, -you might color it better or worse, but it will still be a dog. - -## Primitive types - -In NeoHaskell, you've got the typical primitive types out of the box: - -| Type | Description | Example | -| -------- | -------------------------------- | ------------------------------------------ | -| `Int` | Integer numbers | `42` | -| `BigInt` | Big integer numbers | `1234567890123456789012345678901234567890` | -| `Float` | Simple precision decimal numbers | `3.1415` | -| `Double` | Double precision decimal numbers | `3.141592653589793` | -| `Bool` | True or False | `True` | -| `Char` | Single characters | `'a'` | - -Of course there are much more many types, but you can consider these as the -most basic ones. - -## Annotating Constants - -Until now, when we wanted to create a constant, we just assigned a value to a name. -In the NeoHaskell repl, we could do it like so: - -```haskell -neo> myConstant = 42 -``` - -In this case, the compiler will automatically _infer_ that the type of `myConstant` -is `Int`. But it could be possible that we want it to be of type `BigInt`, instead of -`Int`. - -Given that types are a powerful tool for designing our programs, in NeoHaskell they -get to be on their own line. This means, that for annotating the type of a constant, -we write the type on a line above the assignment line. - -We write the type of a constant by using the `::` symbol, which reads as "is of type". - - - - -```haskell -myConstant :: BigInt -myConstant = 42 -``` - - - - - -```typescript -const myConstant: BigInt = 42; -// Note, `BigInt` does not actually exist in TypeScript -``` - - - - -:::tip -In the NeoHaskell REPL, use the command `:{` to begin writing multiple lines, -use the command `:}` to end the multi-line input. -::: - -If we wanted to try this on the REPL, we could do it like so: - -```haskell -neo> :{ - myConstant :: BigInt - myConstant = 42 - :} - -neo> myConstant -42 -``` - -## Checking the type of something - -In the NeoHaskell REPL, you have a pretty useful command to check the type of stuff, -you can write `:type` and the name of something, and it will tell you the type: - -```haskell -neo> :type myConstant -myConstant :: BigInt -``` - -It replies with "myConstant is of type BigInt". - -## Avoiding primitive obsession - -There's this concept in software development called "primitive obsession" which -essentially says that using primitive types for everything is bad. - -This is because they don't really tell you a story about how your program is -structured, or how your data gets transformed. - -A counter-argument that people usually say against "wrapper types", or types whose -only reason of life is to give a name to a primitive type, is that they are not -very efficient. You now would need to have both the primitive value in memory, and -the wrapper that gives the type a name. - -In NeoHaskell, you get to create these wrapper types with no performance penalty. - -In memory, it is only stored the actual value of the primitive type, but in compilation, -the compiler thinks that it is a different type. - -To create a type of this kind, you use the `newtype` keyword and do it this way: - - - - -```haskell -newtype Age = Age Int -``` - - - - - -```typescript -// Note: In TypeScript there is no such thing as newtype, so the closest thing -// would be to emulate it with a workaround like the following: -type Age = number & { __tag: "Age" }; -const Age = (age: number): Age => age as Age; -``` - - - - -This creates a new type called `Age` whose sole purpose is to be differentiated from -other `Int`s. It also gives us a _constructor function_ called `Age` that we can use to create a -new `Age` value. - -To create a constant with this type, you use it's _constructor function_ before the actual `Int` value: - - - - -```haskell -catAge = Age 2 -``` - - - - - -```typescript -const catAge = Age(2); -``` - - - - -Note how now, you cannot use it as another `Int`: - -```haskell -neo> catAge + 4 - --- TYPE MISMATCH ---------------------------- -Attempting to add two values, but the first value doesn't match the type of the second value: - - catAge + 4 - ^^^^^^ - -`catAge` is of type: - - Age - -But `(+)` needs the 1st argument to be: - - Int - -Hint: Maybe try using `cast`? -``` - -The error message suggests the use of `cast` , which is a function that allows you to -cast a wrapper type to the type that it wraps, with no performance penalty. We can use -it like so: - -```haskell -neo> cast catAge + 4 -6 -``` - -In this case, the compiler knows that `catAge` is of type `Age`, and that `4` is of type `Int`, -so it casts `catAge` to `Int` and then adds it to `4`. - -### Why do I have to cast it? - -The reason for wrapper types is that they not only give names to other types, but also that they -give you a way to differentiate them from the other types. This is useful for when you want to -create functions that only accept a certain subset of types. - -It is common that we create a function that accepts many `Int`s as its arguments, but we -want to differentiate them from each other. For example, we could have a function that -accepts a `weight` and a `distance` as arguments, and we want to differentiate them from each -other. - -Without wrapper types, we could easily mix them up, making the function call to be -incorrect: - -```haskell -neo> :{ - calculateEnergy weight distance = ... - :} - -neo> myWeight = 42 -neo> myDistance = 31415 -neo> calculateEnergy myDistance myWeight -- Oops, we mixed them up! -``` - -To solve this, we could make use of wrapper types, but we also need to know how to specify the types of -the arguments of a function. Let's check that out. - -## Annotating Functions - -Although the compiler doesn't enforce it, you **should** annotate the types of the arguments. Even more, -the NeoHaskell good practices recommends that you write the type of the function **before** beginning to -write the function's implementation. - -> The NeoHaskell good practices recommends that you write the type of the function **before** beginning to -> write the function's implementation. - -This is because, as we mentioned earlier, types are a powerful tool for designing our programs, and -they are a great way to communicate the intent of our functions. But this is only true if we ensure that -we have good design pieces to work with, like the wrapper types we just saw. - -To annotate the arguments and result types of a function, we write the types of the arguments separated by -arrows like `->`, being the last one the result type. For example, if we wanted to annotate the -`calculateEnergy` function, we could do it like so: - - - - -```haskell -calculateEnergy :: Int -> Int -> Int -calculateEnergy weight distance = ... -``` - - - - - -```typescript -const calculateEnergy = (weight: number, distance: number): number => ... -``` - - - - -:::note - -It is common that newcomers find it weird that there's no separation between the arguments and the result type, -like with a parenthesis or something. This is because it is very useful for a thing called _partial application_, -which we will see in future sections. - -::: - -Note how the `calculateEnergy` function accepts two `Int`s as arguments, and returns an `Int` as a result. But we -still have the problem of mixing up the arguments, so let's fix that. - -![types are a lie meme](./img/types_lie.jpeg) - -A good way of measuring whether a type annotation is good or not, is to check if it is possible to figure out the -implementation of the function just by looking at the type annotation. In this case, we can't, because we don't -know which argument is which, neither if we need to pass `weight`, `distance` or even maybe some other `Int` that -we don't know about. - -Given that weight and distance are two units that probably make sense in our domain, we can create wrapper types -for them: - - - - -```haskell -newtype Weight = Weight Int -newtype Distance = Distance Int -``` - - - - - -```typescript -type Weight = number & { __tag: "Weight" }; -const Weight = (weight: number): Weight => weight as Weight; - -type Distance = number & { __tag: "Distance" }; -const Distance = (distance: number): Distance => distance as Distance; -``` - - - - -Now, we can use these wrapper types to annotate the arguments of the `calculateEnergy` function: - - - - -```haskell -calculateEnergy :: Weight -> Distance -> Int -calculateEnergy weight distance = ... -``` - - - - - -```typescript -const calculateEnergy = (weight: Weight, distance: Distance): number => ... -``` - - - - -Now it is much better, but we can improve it even more. Is the weight in kilograms? In pounds? In grams? Is the -distance in meters? In kilometers? In miles? In light years? We don't know, and we can't know just by looking at -the type annotation. Also, we have no clue about the result type, is it in joules? In calories? In kilocalories? - -Let's fix that by creating more wrapper types, and changing the type annotation of the function: - - - - -```haskell -newtype Kilograms = Kilograms Int -newtype Meters = Meters Int -newtype Joules = Joules Int - -calculateEnergy :: Kilograms -> Meters -> Joules -calculateEnergy weight distance = ... -``` - - - - - -```typescript -type Kilograms = number & { __tag: "Kilograms" }; -const Kilograms = (weight: number): Kilograms => weight as Kilograms; - -type Meters = number & { __tag: "Meters" }; -const Meters = (distance: number): Meters => distance as Meters; - -type Joules = number & { __tag: "Joules" }; -const Joules = (energy: number): Joules => energy as Joules; - -const calculateEnergy = (weight: Kilograms, distance: Meters): Joules => ... -``` - - - - -This is much better, if we take a quick glance at the type annotation, it is very clear what the function does, -and what it expects as arguments and what it returns as a result. - -```haskell -calculateEnergy :: Kilograms -> Meters -> Joules -``` - -We haven't looked at the implementation, and it really doesn't matter, because there's a **contract** that has been -specified for this function. It is a contract that we can trust, because it is enforced by the compiler. - -And remember! Functions in NeoHaskell cannot fail, so if you see a function that returns a `Joules`, you can be -sure that it will return a `Joules` value **always**, and it will never crash. - -This is the true power of types, they are a powerful tool for designing our programs, and they are a great way to -remove the need of having to read the implementation of a function to understand what it does. - -## Conclusion - -In this section, we've seen how to annotate the types of constants and functions, and how to create wrapper types -to avoid primitive obsession. - -We have dipped our toes into what they call [Domain Driven Design](https://en.wikipedia.org/wiki/Domain-driven_design) -or DDD, which is the way of designing software that NeoHaskell inspires from. - -NeoHaskell's way of approaching software development is very different to other programming languages, because it makes -you think about the design of your program before you even start writing it. At first, it might seem like a lot of work, -but in exchange, you get tools that make your life easier, and your head free of worries. diff --git a/docs/extra-topics/_category_.json b/docs/extra-topics/_category_.json deleted file mode 100644 index 16cc066..0000000 --- a/docs/extra-topics/_category_.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "label": "Extra Topics", - "position": 99, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "Here we talk about other topics that are related to NeoHaskell, but are not directly related to helping you understand the language." - } -} diff --git a/docs/extra-topics/faq.mdx b/docs/extra-topics/faq.mdx deleted file mode 100644 index cabe222..0000000 --- a/docs/extra-topics/faq.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -sidebar_position: 0 ---- - -# FAQ - -This page compiles questions that are commonly asked on [Discord](https://discord.com/invite/wDj3UYzec8). - -If your question is not listed here, feel free to reach out in the chat so -the community can help you find the way. - -## Who's the target audience for NeoHaskell? - -Software developers that are comfortable (not experts) with a statically typed mainstream -language, who are interested in developing their own projects easily. - -You can find more in-depth info in the [NHEP 2](/blog/0002-project-target). diff --git a/docs/extra-topics/integrating-neohaskell.mdx b/docs/extra-topics/integrating-neohaskell.mdx deleted file mode 100644 index f8553b9..0000000 --- a/docs/extra-topics/integrating-neohaskell.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -sidebar_position: 4 ---- - -import Badge from "@site/src/components/Badge"; - -# Integrating NeoHaskell - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -TODO - -:::caution -**UNDER CONSTRUCTION** - -![Work in progress](../img/construction-worker.gif) -::: diff --git a/docs/extra-topics/neohaskell-genesis.mdx b/docs/extra-topics/neohaskell-genesis.mdx deleted file mode 100644 index 9539053..0000000 --- a/docs/extra-topics/neohaskell-genesis.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -sidebar_position: 1 ---- - -# The NeoHaskell Genesis - -## The Beginning: Complexity Generation - -Once upon a time, there was a developer who embarked on a project written in Haskell—a language known for its expressive power and strong type system. Consumed by the allure of purely functional programming, this developer started off with enthusiasm and hope. Yet as time passed, the Haskell codebase, once a work of intellectual beauty, turned increasingly cumbersome and enigmatic, as the team -kept adding on newer and fancier features. - -The project was eventually pulled from production, but its lessons were far from lost. Over the subsequent five years, the developer turned to TypeScript, Python, and Java—languages celebrated for their wide utility and enterprise appeal. While the code-bases in these languages weren't inherently complex in their syntax, a new challenge emerged: cognitive overload, exacerbated by the side effects and unpredictability that came with these languages. Though the lines of code were straightforward, the resulting complexity was covertly overwhelming. - -## The Awakening: NeoHaskell Emerges - -Confronted by these extremes, it became clear that a middle ground was sorely needed. And so, the concept of NeoHaskell was conceived, drawing wisdom from experiences in Domain-Driven Design, Event Sourcing, CQRS, and the craftsmanship intrinsic to software development. - -The ambition? To rebuild Haskell from its foundations, creating a language designed to combat both code complexity and accidental complexity—those problems that don't arise from the domain but from the tools and methodologies employed. - -## The goal: Focus on what's important - -### Beginner-Friendliness - -The first lesson from the initial Haskell venture was its steep learning curve. NeoHaskell, therefore, aims to be approachable. It adopts a syntax and paradigms that are less intimidating for newcomers while still preserving the essential capabilities that Haskell offers. - -### Productivity - -In crafting NeoHaskell, an emphasis has been placed on developer productivity. It encourages the creation of rich, bounded contexts and aggregate roots, allowing the developer to focus more on solving actual domain problems rather than wrestling with technicalities. - -### Complexity Mitigation - -Inspired by Command-Query Responsibility Segregation and Event Sourcing design patterns, NeoHaskell promotes a clean architecture where read and write operations are distinctly separated. By keeping an immutable record of state changes, it brings predictability into the system, thereby mitigating both cognitive and code complexity. - -### Joy of Use - -NeoHaskell is not merely a tool; it is a craft. It aspires to make coding not just functional but also pleasurable. The language aims to trigger a flow state, making each moment of development a joyful experience. - -## The NeoHaskell Promise - -As it continues to evolve, NeoHaskell aspires to be a nexus of balance: simple yet potent, utilitarian yet principled, designed to cut through complexity while offering a pleasurable user experience. It doesn't just aim to be another programming language; it hopes to be a landmark in the landscape of software development—a testament to the hard-won lessons from a history rife with complexities. diff --git a/docs/extra-topics/news-announcements.mdx b/docs/extra-topics/news-announcements.mdx deleted file mode 100644 index 6066c00..0000000 --- a/docs/extra-topics/news-announcements.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -sidebar_position: 2 ---- - -# News and Roadmap - -Here you can find news about the core NeoHaskell project. For real time updates, you should probably join the [Discord server](https://discord.com/invite/wDj3UYzec8), or follow the [NeoHaskell Twitter account](https://x.com/neohaskell). - -## Announcements - -- [NeoHaskell 0.7.0 - Event Store, Enhanced Testing, and Developer Experience](https://github.com/neohaskell/NeoHaskell/releases/tag/0.7.0) -- [NeoHaskell 0.6.0: Project Scaffolding, Asynchronous Tasks, and Smarter Error Handling](https://github.com/neohaskell/NeoHaskell/releases/tag/0.6.0) -- [NeoHaskell v0.5.0 - HTTP, TOML, Nested Actions Now Executed and Major Refactoring](https://dev.to/neohaskell/neohaskell-v050-http-toml-nested-actions-now-executed-and-major-refactoring-4cb6) -- [NeoHaskell v0.4.0: Update with Concurrency Fixes and Architectural Improvements](https://dev.to/neohaskell/neohaskell-v040-update-with-concurrency-fixes-and-architectural-improvements-267b) -- [Understanding Triggers in NeoHaskell: A Gateway to Event-Driven Programming](https://dev.to/neohaskell/understanding-triggers-in-neohaskell-a-gateway-to-event-driven-programming-49nb) -- [Introducing NeoHaskell v0.3.0: Triggers, Actions, and Services](https://dev.to/neohaskell/introducing-neohaskell-v030-triggers-actions-and-services-2ae) -- [Announcing NeoHaskell v0.2.0: Bringing Elm-Inspired Architecture to CLI Apps](https://dev.to/neohaskell/announcing-neohaskell-v020-bringing-elm-inspired-architecture-to-cli-apps-54db) -- [DevLog 00002 - A main loop working](https://dev.to/neohaskell/devlog-00002-a-main-loop-working-31kj) -- [DevLog 00001 - Command Line Updates](https://dev.to/neohaskell/devlog-00001-command-line-updates-24f3) -- [Introducing NeoHaskell: A beacon of joy in a greyed tech world -](https://dev.to/neohaskell/introducing-neohaskell-a-beacon-of-joy-in-a-greyed-tech-world-4f9b) - -## Roadmap - -The roadmap represents a rough view of the progress of the different -parts of the project. If one box is checked doesn't mean it is 100% -done, as software usually is an infinite work, it means that it is -usable, but always with room for improvement. - -- [x] Array -- [x] Basics -- [x] Bytes -- [x] Char -- [x] Function utils -- [x] IO -- [x] Int -- [x] LinkedList -- [x] Map -- [x] Maybe -- [x] Record -- [x] Result -- [x] Text -- [x] Tuple -- [x] Unit -- [x] Unknown -- [x] Var -- [x] Version -- [x] Async IO operations - Should be reworked into Async Tasks -- [x] Channel -- [x] ConcurrentVar -- [x] Action Handling -- [x] Trigger Handling -- [ ] Task core module -- [ ] File tasks core module -- [ ] Path tasks core module -- [ ] Subprocess tasks core module -- [ ] Build CLI command -- [ ] Autoinstaller -- [ ] Test command -- [ ] Run command -- [ ] Time core module -- [ ] File Actions -- [ ] Path Actions -- [ ] Subprocess Actions -- [ ] Time Actions -- [ ] Http (Tasks) -- [ ] Http (Actions) -- [ ] Event Handling (needs to be reworked) -- [ ] View/HTML Handling -- [ ] Traits (TBD) -- [ ] REPL CLI command diff --git a/docs/getting-started/_category_.json b/docs/getting-started/_category_.json deleted file mode 100644 index d80aef7..0000000 --- a/docs/getting-started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Getting Started", - "position": 1, - "collapsed": false -} diff --git a/docs/getting-started/dogma.mdx b/docs/getting-started/dogma.mdx deleted file mode 100644 index abddf08..0000000 --- a/docs/getting-started/dogma.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -sidebar_position: 1 ---- - -# The NeoHaskell Dogma - -> A Dogma is not bad if it is aligned with the principles of the individual, and flexible enough for the moments it isn't. - -## The NeoHaskell Statements - -* Beautiful is better than ugly -* Simple is better than complex -* Duplicate and easy is better than concise and complex -* Mathematics are good when not seen -* The 95% of type-safety should be done with 5-or-less% of the effort -* Instead of investing effort in the other 5%, better release to prod -* If it takes more than 15 minutes to understand, it's a bug -* The functional way is better, unless the imperative one is easier - -Curious about the above? Keep reading! diff --git a/docs/getting-started/img/trade-offer.png b/docs/getting-started/img/trade-offer.png deleted file mode 100644 index 80ae171..0000000 Binary files a/docs/getting-started/img/trade-offer.png and /dev/null differ diff --git a/docs/getting-started/installing-neohaskell.mdx b/docs/getting-started/installing-neohaskell.mdx deleted file mode 100644 index 4871f8a..0000000 --- a/docs/getting-started/installing-neohaskell.mdx +++ /dev/null @@ -1,134 +0,0 @@ ---- -sidebar_position: 3 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; - -# Installing NeoHaskell - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -All of NeoHaskell's tasks are handled by the **Neo** command line tool. You might think of it as the chosen one _(The Matrix™️ pun, sorry)_. - -Neo will install all the required stuff for you, so you don't have to worry about it. - -## Getting Neo - -In order to install Neo, copy and paste the following command in your terminal (if you're on Windows, use PowerShell as Administrator): - - - - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.neohaskell.org | bash -``` - - - - - -:::note -For many commands, Neo uses `git` under the hood. If you don't have it, you -can read a guide on how to install it -[here](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). -::: - -## Trying the Installation - -Once everything is installed, usually the common thing is to try that -some commands do work. Let's do it the opposite way, let's try some -command that doesn't exist: - -```text -$ neo learn-kung-fu - -Neo: I'm sorry, I don't know how to `learn-kung-fu`. - Try `neo help` to see what I can do. - - It looks like your last command failed. Remember, if it is taking - you more than 15 minutes to figure it out, it is a bug in the system. - - Please go to: - - https://github.com/neohaskell/neohaskell/issues/new - - And report it. I'll be waiting for you. -``` - -Remember that, if you're having trouble figuring stuff out, and it is -taking you more than 15 minutes, we consider it a bug in the system. - -Let's actually try the installation now by running `neo version`: - -```text -$ neo version -v0.1.0 -``` - -## Updating Neo - -Neo is a very young project, so it is constantly changing. In order to -keep up with the latest changes, you can run `neo update:self`: - -```text -$ neo update:self -Neo: Updating Neo........................... - - The Matrix feels different after this update. -``` - -## Using Neo Effectively - -Neo is a very powerful tool, and it can do a lot of things. In order to -learn how to use it, you can run `neo help` to see all the available -commands: - -```text -$ neo help -Neo: I'm here to help you. - - Usage: neo [options] - - Commands: - help Show this help message - version Show version - new Create new things - update Update things - build Build things - run Run things - watch Run things on file changes - clean Clean the project - install Install a package - uninstall Uninstall a package - search Search for a package - list List installed packages -``` - -What does these commands that say "things" mean? Well, they are -commands that have subcommands. If we run `neo run help`, we'll see -something like this: - -```text -$ neo run help -Neo: I'm here to help you. - - Usage: neo run [options] - - Commands: - app Run the application - repl Run the interactive console - test Run the tests - format Run the formatter -``` - -You can keep using the `help` command to see what each command does! - -## Next Steps - -Now that you have Neo installed, we're going to start getting our hands -dirty, with the Neo interactive console. Let's go to the next section! diff --git a/docs/getting-started/interactive-console.mdx b/docs/getting-started/interactive-console.mdx deleted file mode 100644 index 8859361..0000000 --- a/docs/getting-started/interactive-console.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -sidebar_position: 4 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; - -# Playing with the REPL - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -Even though NeoHaskell is a compiled language, it provides an interpreted -mode which is much faster for the development process. In any moment, you -can run `neo run:repl` and it will start an interactive console where you -can checkout small pieces of NeoHaskell code. - -![trade offer meme with a repl](img/trade-offer.png) - -When you run the command, it will start, what's called in programmer jargon, -a REPL (Read-Eval-Print-Loop). A REPL will do the following algorithm: - -1. **Read** the code you wrote -2. **Evaluate** (execute) it -3. **Print** the result -4. **Loop** back to 1 - -Let's fire up `neo run:repl`: - -```text -neo> -``` - -This is the prompt of the REPL. It's waiting for you to write some code. Let's write our first hello world program! - -```text -neo> print "Hello World!" -Hello World! -``` - -Awesome, we've completed our first hello world program under a minute. Let's try doing some math: - -```text -neo> 1 + 1 -2 - -neo> 2 * 2 -4 - -neo> 7 ** 20 -79792266297612001 -``` - -Whoops, the last number is a bit too big. -This is because `**` is the exponentiation operator. -It's the same as writing $7^{20}$ in math. - -Anyway, to quit the REPL, you can press `Ctrl + C` or write `:quit`. - -## Exploring the REPL commands - -At any time, you can write `:help` to see the list of commands available. - -Don't worry too much about them if they -seem a bit overwhelming. You'll learn them as you go. - -In the next sections, we will start -trying NeoHaskell from the REPL. diff --git a/docs/getting-started/intro.mdx b/docs/getting-started/intro.mdx deleted file mode 100644 index aee9258..0000000 --- a/docs/getting-started/intro.mdx +++ /dev/null @@ -1,113 +0,0 @@ ---- -sidebar_position: 2 ---- - -import Badge from "@site/src/components/Badge"; - -# Introduction - -:::caution -The documentation that you're reading is a design document where most of -the features you're reading are yet to be implemented. Check the [Note on the Docs](/docs/docs-intro) -::: - -## What is NeoHaskell? - -NeoHaskell is a language for building and maintaining applications with the minimal amount -of headaches possible. It builds on top of industrial-grade tools like GHC to allow developers of any level to be productive in a wide range of fields. -It compiles to native code, and runs natively on your machine, allowing it -to interoperate natively with languages like C, as well as it's way of working makes integration with other codebases very easy. - -Here's what it looks like: - -```haskell -"NeoHaskell is cool" - |> String.replace "cool" "awesome" - |> String.toWordsList - --- > ["NeoHaskell", "is", "awesome"] -``` - -As you can see in the example above, NeoHaskell favors **semantic and readable code** that is easy to understand, while providing tools that guide the developer towards a more maintainable solution. - -By reading the code, it is easy to infer what it is doing. There's no need for code comments, because the code is self-explanatory. - -You might have some questions already, don't worry about it. We will cover -every little detail in the rest of the docs. Just read along so you can have -a high-level understanding of what NeoHaskell offers. - -:::info Prerequisites -The NeoHaskell documentation is tailored for beginners, but it does make some assumptions like familiarity with a text editor like [Visual Studio Code](https://code.visualstudio.com/), -at least some level of fluency with the terminal (like changing directories and executing commands), and at least that some other experience with coding -in general. - -Many examples will be accompanied with a counter example in TypeScript, it is not a requirement to know TypeScript, but it helps as it is a good middle-ground to make an anchor for helping you -learn through similarity. -::: - -## A Batteries-Included Language - -NeoHaskell is a language, a framework, and ecosystem that tries to aid in -software development for a wide range of fields. While some might argue that -it is not the best tool for each of those specific tasks, it for sure is the -one that makes the development process more enjoyable. - -It could be possible that you are an experienced developer, and you have -an already working application. NeoHaskell allows you easily integrate it -into many applications, you can read more about it in the [Integrating NeoHaskell](/docs/extra-topics/integrating-neohaskell.mdx) page. - -We all left a coding -project for a period of time and then found ourselves completely lost when -coming back into it. NeoHaskell mitigates that by adopting a strong opinion -on how stuff should be done, and in exchange, you get back a predictable -application that is easy to extend and maintain. - -NeoHaskell aims to help you in fields as: - -- Backend Development -- Command Line Scripting -- Website Pages -- Mobile/Desktop Apps - -Those look like completely disconnected fields that don't have much in -common. In reality, the NeoHaskell way of working works well with all of -them, and makes everything a bliss. - -NeoHaskell comes with a extensive core library that helps you with a wide -range of tasks like: - -- File System Management -- Command Line Interfaces -- Create HTTP APIs -- Consume HTTP APIs -- Serialization for JSON and friends -- HTML Rendering - -And much more! The NeoHaskell compiler allows you to produce optimized -builds that only have the required code that your app needs. - -## A User-Centered Language - -Everything in NeoHaskell is designed and implemented with the user in mind. -Therefore, everything is packed into the `neo` command line tool. - -What is everything? Well, everything that you need for developing your -application: - -- Installing NeoHaskell -- Generating a new project -- Generating commonly-shaped files -- Generating documentation -- Managing dependencies -- Compiling your code -- Linting -- Formatting -- Running your tests -- Packing your application for deployment - -And every use case that helps you to be more happy along the way. - -## Begin your Journey - -This has been a high-level introduction into the language, now we will -roll up our sleeves and get our hands dirty. Happy hacking! diff --git a/docs/modules/_category_.json b/docs/modules/_category_.json deleted file mode 100644 index c7739e6..0000000 --- a/docs/modules/_category_.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "label": "Core Modules", - "position": 98, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "Guides and tutorials to help you understand the built-in modules in NeoHaskell" - } -} diff --git a/docs/modules/command-parsing.mdx b/docs/modules/command-parsing.mdx deleted file mode 100644 index 3b6b1b4..0000000 --- a/docs/modules/command-parsing.mdx +++ /dev/null @@ -1,250 +0,0 @@ ---- -sidebar_position: 0 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; -import Badge from "@site/src/components/Badge"; - -# Building Command-Line Interfaces - -Most programs we write do something. And a lot of the time, we want to **ask the user what they want to do** — maybe by passing a name, setting a path, or toggling a feature on or off. - -That’s where NeoHaskell’s command-line parser comes in. It's clean, safe, and easy to use — and integrates perfectly with the rest of your application. - -You describe what arguments you want, and NeoHaskell builds a structured object for you, automatically. - -Let’s see how. - -## A Simple CLI: Hello, Whoever You Are! - -Imagine we’re writing a small tool. It greets someone. The user can give us their name, and ask us to shout it. - -Here’s how we describe that in NeoHaskell. - -```haskell -data GreetCommand = GreetCommand - { name :: Text, - isShouting :: Bool - } -``` - -That’s our data. A name (`Text`) and whether to shout (`Bool`). - -Now the command parser: - -```haskell -import Command qualified - -greetParser : Command.OptionsParser GreetCommand -greetParser = do - parsedName <- Command.text - { help = "Name of the person to greet", - long = "name", - short = 'n', - metavar = "NAME", - value = Nothing - } - parsedShout <- Command.flag - { help = "Whether to shout the greeting", - long = "shout", - short = 's', - value = Nothing - } - GreetCommand { name = parsedName, isShouting = parsedShout } - |> Command.yield -``` - -So far, so good. - -We just defined how to convert a few flags into something meaningful: - -`GreetCommand { name = "Alice", isShouting = True }` - -Now, we'd need a function to know how to run this command. We usually call these _handlers_: - -```hs -handleCommand : GreetCommand -> Task Text Unit -handleCommand GreetCommand{name, isShouting} = do - let baseGreeting = [fmt|Hello, {name}!|] - let greeting = - if isShouting then - baseGreeting |> Text.toUpper - else - baseGreeting - Console.print greeting -``` - -Afterwards, we'd need to modify our `run` function in order to parse the command and call the handler: - -```hs -run : Task Text Unit - cmd <- Command.parseHandler greetParser - - let onError err = [fmt|Oops, an error has occurred{e}|] - handleCommand cmd - |> Task.mapError onError -``` - -Try running the code with: - -```bash -$ neo run -- --name Alice --isShouting -``` - -:::note -When running code with `neo run`, if your program receives command line arguments, you should write them -after two dashes like above. If not, `neo` will think that you are passing those to it. -::: - -## Multiple Commands - -Sometimes, you want more than one thing your program can do. Like `greet`, but also `wave` or `thank`. - -You can define a data type that lists all possible commands: - -```haskell -data Command - = Greet GreetCommand - | Wave WaveCommand - | Thank ThankCommand -``` - -Each command has its own record: - -```haskell -data GreetCommand = GreetCommand - { name :: Text, - isShouting :: Bool - } - -data WaveCommand = WaveCommand - { emoji :: Text - } - -data ThankCommand = ThankCommand - { name :: Text - } -``` - -Now, let’s write a parser for each one. - -```haskell -greet : Command.CommandOptions Command -greet = - { name = "greet", - description = "say hello", - version = Nothing, - decoder = do - name <- Command.text - { help = "Who to greet", - long = "name", - short = 'n', - metavar = "NAME", - value = Nothing - } - isShouting <- Command.flag - { help = "Whether to shout", - long = "shout", - short = 's', - value = Nothing - } - - -- Protip! If there's a variable in scope with the same name as the record field, - -- you don't have to assign it. NeoHaskell will do it for you! - GreetCommand { name, isShouting } - |> Greet -- Note how we're wrapping the type GreetCommand - -- into the Greet branch of the Command enum. - |> Command.yield - } -``` - -```haskell -wave : Command.CommandOptions Command -wave = - { name = "wave", - description = "wave silently", - version = Nothing, - decoder = do - emoji <- Command.text - { help = "Which emoji to use", - long = "emoji", - short = 'e', - metavar = "EMOJI", - value = Just "👋" - } - WaveCommand { emoji } - |> Wave -- Same here - |> Command.yield - } -``` - -```haskell -thank : Command.CommandOptions Command -thank = - { name = "thank", - description = "thank someone", - version = Nothing, - decoder = do - name <- Command.text - { help = "Who to thank", - long = "name", - short = 'n', - metavar = "NAME", - value = Nothing - } - ThankCommand { name } - |> Thank - |> Command.yield - } -``` - -Now combine them into a single parser: - -```haskell -allCommands : Command.OptionsParser Command -allCommands = - Command.commands [greet, wave, thank] -``` - -And finally run it: - -```haskell -main : Task Text Unit -main = do - userCommand <- Command.parseHandler - { name = "hello-cli", - description = "A friendly assistant", - version = Just [version|1.0.0|], - decoder = allCommands - } - let onError e = [fmt|Oops, something went wrong: {e}|]) - handle userCommand - |> Task.mapError onError -``` - -## Handling the Commands - -Once you've parsed a command, you can handle it however you want: - -```haskell -handle : Command -> Task Text Unit -handle command = - case command of - Greet GreetCommand{name, isShouting} -> do - let baseGreeting = [fmt|Hello, {name}!|] - let greeting = - if isShouting then - baseGreeting |> Text.toUpper - else - baseGreeting - Console.print greeting - - Wave WaveCommand{emoji} -> - Console.print emoji - - Thank ThankCommand{name} -> - Console.print [fmt|"Thanks, {name}!"|] -``` - -And that’s it! Now your CLI supports multiple commands, each with its own set of options, and a clean entrypoint to handle them. diff --git a/docusaurus.config.js b/docusaurus.config.js index 8e937b1..26ae00b 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -119,12 +119,7 @@ const config = { type: "docSidebar", sidebarId: "tutorialSidebar", position: "left", - label: "Documentation", - }, - { - to: "blog", - label: "Evolution Proposals", - position: "left", + label: "Support Us!", }, { href: "https://github.com/neohaskell/", @@ -141,15 +136,6 @@ const config = { footer: { style: "dark", links: [ - { - title: "Docs", - items: [ - { - label: "Get Started", - to: "/docs/docs-intro", - }, - ], - }, { title: "Community", items: [ @@ -158,8 +144,12 @@ const config = { href: "https://discord.com/invite/wDj3UYzec8", }, { - label: "Twitter", - href: "https://twitter.com/neohaskell", + label: "X", + href: "https://x.com/neohaskell", + }, + { + label: "LinkedIn", + href: "https://www.linkedin.com/company/neohaskell", }, ], }, diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 6fd8fe4..727b691 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -24,10 +24,9 @@ function HomepageHeader() {

- A language that triggers -
- - flow state. + Software Development Is Broken.{" "} + + We're Fixing It From The Language Up.

@@ -35,31 +34,342 @@ function HomepageHeader() {
-

+

+ Every successful software company follows the same tragic arc: +

+

+ - Day one: pure creativity, features ship in hours, + everyone understands everything. +

+

+ - Year one: velocity slows, meetings multiply, "technical debt" + enters the vocabulary. +

+

+ - Year three: the rewrite discussions begin. +

+

+ - Year five: half your budget maintains what the other half tries to + replace. +

+

+ This isn't incompetence. It's not bad management. It's the + inevitable result of building on foundations that turn human + thoughts into machine instructions through layers of mistranslation. +

+

The Broken Telephone Architecture

+

+ Watch how a simple business need travels through your organization: +

+

+ The CEO says: "When customers upgrade, we should celebrate with + them." +

+

+ The CTO interprets: "Upon plan upgrade, trigger celebration + workflow." +

+

+ The Product Manager writes: "Implement upgrade handler with + notification service integration." +

+

+ The Developer codes:{" "} +

+              
+                UPDATE users SET plan = 'premium'; INSERT INTO notifications...
+              
+            
+

+

+ Six months later, nobody remembers why that code exists. The + celebration feature breaks. The developer who wrote it left. The new + developer is afraid to touch it. The cycle of decay has begun. +

+

+ This isn't a people problem. It's a technology problem. We're + forcing humans to speak machine when machines should speak human. +

+

Technology Shapes Thought

+

+ The tools we use shape how we think. When your language forces you + to express everything as CREATE, READ, UPDATE, DELETE, you start + seeing the world as records to manipulate rather than events that + happen. You stop thinking "customer upgraded" and start thinking + "update customer record." +

+

+ This subtle shift is poison. It disconnects your code from your + business. It makes simple things complex. It makes change dangerous. + It makes your software brittle where it should be antifragile. +

+

+ The oldest ideas survive because everything unnecessary has been + stripped away. Double-entry bookkeeping has worked for 700 years. + Event ledgers are how humans naturally think about change. Banks + don't UPDATE your balance; they record deposits and withdrawals. + History doesn't UPDATE the past; it records what happened. +

+

+ Why did we abandon this natural model for CRUD? +

+

+ Because storage was expensive. Because computers were slow. Because + we optimized for machines instead of humans. +

+

+ Those constraints are gone. It's time for software development to + become human again. +

+

+ Enter NeoHaskell: Where Business Events Become Code +

+

+ NeoHaskell isn't just another programming language. It's a + fundamental rethinking of how human intention becomes running + software. +

+

+ When your business team says "customer plan upgraded" that's + literally how you write it: +

+

+ + {`data CustomerPlanUpgraded = CustomerPlanUpgraded + { customer :: CustomerId, + fromPlan :: Plan, + toPlan :: Plan, + upgradeDate :: DateTime + } +`} + +

+

+ No translation. No abstraction. No impedance mismatch. Your business + logic reads like a business conversation. +

+

What Makes NeoHaskell Different

+

+ Event Sourcing Is The Language, Not A Library +

+

+ Other languages make you fight for event sourcing. You need + frameworks, libraries, careful discipline. One mistake and you're + back in CRUD hell. +

+

+ In NeoHaskell, event sourcing isn't something you add. It's + something you'd have to work to avoid. Every state change is an + event. Every event is immutable. Every system is automatically + auditable, replayable, debuggable.{" "} - NeoHaskell is a dialect of Haskell that is focused on - newcomer-friendliness and productivity. + The compiler doesn't let you store something in the database where + you're not supposed to.

-

- It is designed to be easy to learn and use, while also being - powerful enough to release your app with minimum effort and maximum - confidence. +

+ Mathematical Guarantees, Human Expression +

+

+ From Haskell, we inherit something profound: if it compiles, it + works. This isn't marketing, it's math. Entire categories of errors + become impossible. Race conditions, null pointer exceptions, type + mismatches, all gone. +

+

+ But unlike Haskell, we've optimized for developer happiness, and we + chase your success at all costs. The standard library feels + familiar. There are no millions of operators to memorize. The + concepts map to how you already think. The error messages actually + help. +

+

+ Linear Complexity In A Exponential World +

+

+ In traditional systems, feature #100 might take 10x longer than + feature #10. Dependencies tangle. Side effects multiply. Fear creeps + in. +

+

+ In NeoHaskell, feature #1000 takes the same effort as feature #1. + New events don't break old ones. New handlers don't destabilize + existing ones. Your system grows by addition, never by modification. +

+

Where We Are Today

+

+ NeoHaskell is not complete. We're building this in public, with real + production users, solving real problems. +

+

What's Already Working:

+

+ - A Beautiful Standard Library: Carefully crafted APIs that make + simple things trivial and complex things possible. Every function + documented, every pattern thoughtful. +

+

+ - Production-Ready Concurrency: Channels for message passing. Locks + when you need them. Thread-safe variables that actually work. These + are the primitives NeoHaskell is built on top of, ensuring your + system is not only correct, but also blazing fast. +

+

+ - In-Memory Event Store: Perfect for development and testing. Your + events are safe, queryable, replayable. +

+

+ - PostgreSQL Event Store (In Progress): Because production needs + production databases. Built on boring, bulletproof technology. + Coming together beautifully. +

+

What's Coming:

+

+ We're building toward a complete platform that generates everything + you need from your event model. Automatic API generation. + Infrastructure that scales. Deployment that just works. The ease of + low-code with the power of real programming. +

+

+ Imagine describing your business events and getting: +

+

+ - A GraphQL (or Swagger, or gRPC, or...!) API that updates in + real-time +

+

+ - A scalable infrastructure that handles millions of events +

+

+ - Complete observability—know everything that ever happened +

+

+ - Time travel debugging—replay any scenario instantly +

+

+ - The ability to fork a stream of events from your production + environment to your development environment for debugging +

+

+ - Perfect audit trails—compliance becomes trivial +

+

+ This isn't fantasy. We've already done parts of this in the past. + With every release, more becomes automatic. +

+

+ The Philosophy: Remove Everything That Gets In The Way +

+

+ We're not adding features for their own sake. Every decision follows + a simple principle: does this remove friction between human + intention and running software? +

+

+ - Configuration files? Removed. Your code should express intent + clearly enough. +

+

+ - Dependency hell? Removed. One tool, one purpose, one way that + works. +

+

+ - DevOps complexity? Removed. From code to cloud in one command. +

+

+ - The fear of change? Removed. If it compiles, it works. +

+

Who This Is For

+

+ You're a startup founder who knows that initial technical decisions + determine your company's fate. You can't afford to be rewriting in + two years when you should be scaling. +

+

+ You're an enterprise architect tired of managing complexity instead + of creating value. You want systems that grow gracefully, not ones + that calcify. +

+

+ You're a developer who came to this field to build, not to debug. + You want to write business logic, not boilerplate. +

+

+ You're a product manager exhausted by "technical limitations." You + want your ideas to become features, not tickets that languish in + backlogs. +

+

+ You're a CEO who suspects you're spending too much on engineering + for too little output. You're right. And it's not your team's fault. +

+

The Invitation

+

+ We're not asking you to trust us blindly. We're building this with + real companies, solving real problems, in production, today. +

+

+ If you're tired of: +

+

+ - Features that get exponentially harder to implement +

+

+ - Teams that grow faster than output +

+

+ - Systems that everyone fears to change +

+

+ - The constant specter of "the rewrite" +

+

+ Join us. +

+

+ Software development should be joyful, not painful. Systems should + become more valuable over time, not more dangerous. Technology + should amplify human creativity, not constrain it. Your 1000th day + of development should feel like your first. +

+

Start Today

+

+ NeoHaskell is ready for pioneers. If you're building something new, + if you're ready to escape the rewrite trap, if you believe software + can be better: We're waiting for you. +

+

+ The revolution isn't coming. It's being typed right now, one event + at a time, by developers who refuse to accept that suffering is the + price of scale. +

+

+ + Welcome to NeoHaskell. Welcome to software development as it + should be. + +

+

+ + The old world optimizes for machines. We're optimizing for humans. + Join us. +

- + - + @@ -92,10 +402,10 @@ export default function Home(): JSX.Element { return (
- + {/* */} -
+ {/*
- + */}
);