Giter Site home page Giter Site logo

neohaskell / neohaskell Goto Github PK

View Code? Open in Web Editor NEW
253.0 14.0 3.0 196 KB

⏩ NeoHaskell is a dialect of Haskell that is focused on newcomer-friendliness and productivity.

Home Page: https://neohaskell.org

License: Apache License 2.0

ai beginner-friendly command-line concurrency developer-experience domain-driven-design event-sourcing functional-programming haskell mobile

neohaskell's Introduction

NeoHaskell

NeoHaskell is a dialect of Haskell that is focused on newcomer-friendliness and productivity.

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.


Welcome

This is where the NeoHaskell code will live, once the implementation begins.

If you're confused, this is because the project prioritizes the design and documentation first, and only implementation afterwards!

Take a look at the GitHub Milestones page for a rough plan for the implementation.

Design documents are being worked on currently for sharing progress in a stable way with the community. Sorry for the inconvenience! 🙏

Collaborate on Discord

It's always better to hack with people, so why not joining the Discord server?

neohaskell's People

Contributors

nickseagull avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

neohaskell's Issues

Package Management features

  • install: Install packages
  • uninstall: Uninstall packages
  • update: Update packages
  • list: List installed packages

Design tasks for Event Sourcing Documentation

Reasoning

Event Sourcing and Command Query Responsibility Segregation (CQRS) are architectural patterns that are especially relevant for scalable, distributed, and resilient systems. Haskell, with its strong type system and functional nature, can be an excellent fit for implementing these patterns. However, due to their complexity and the multitude of choices available in Haskell, documenting how to create Event Sourcing and CQRS applications can provide invaluable guidance.

Here are some key considerations for creating such documentation:

  1. Complexity: Event Sourcing and CQRS are non-trivial architectures that involve multiple components. The documentation needs to break down this complexity into manageable parts.

  2. Practical Examples: Theoretical explanations should be complemented by real-world examples that demonstrate the implementation of Event Sourcing and CQRS in Haskell applications.

  3. Libraries and Frameworks: If there are Haskell libraries or frameworks that facilitate implementing these patterns, these should be discussed and contrasted.

  4. Performance: Given that one of the appeals of Event Sourcing and CQRS is scalability, performance considerations are crucial.

  5. Consistency and Scalability: Explain how these patterns help in maintaining consistency and scalability and how they might be implemented in Haskell.

  6. Testing and Maintenance: These architectures can be complex to test and maintain, so offering guidance here is vital.

Tasks for "Document creating event sourcing and CQRS applications with Haskell"

Planning and Structure

  1. Identify the scope and boundaries of the documentation. Decide what will be covered and what will be left for future updates or other documents.
  2. Define the target audience and establish prerequisites for understanding the guide.
  3. Sketch out the primary sections and subsections that the documentation will contain.

Content Creation

  1. Introduction:

    • Briefly introduce Event Sourcing and CQRS, and why they are relevant in the modern application landscape.
  2. Conceptual Overview:

    • Explain the core concepts behind Event Sourcing and CQRS, possibly contrasting them with more traditional architectures.
  3. Why Haskell?:

    • Discuss the benefits and challenges of using Haskell for implementing these patterns.
  4. Getting Started:

    • Step-by-step tutorial or walkthrough for setting up a basic Event Sourcing and CQRS system in Haskell.
  5. Haskell Libraries and Frameworks:

    • Discuss existing Haskell libraries and frameworks that facilitate implementing Event Sourcing and CQRS, along with their pros and cons.
  6. Building Blocks:

    • Detail the basic building blocks for creating Event Sourcing and CQRS systems in Haskell.
      • Event Stores
      • Commands and Queries
      • Aggregates
      • Projections
  7. Advanced Topics:

    • Discuss more advanced or optional features like snapshots, event versioning, and integration with message brokers.
  8. Performance Considerations:

    • Discuss how to optimize the performance of an Event Sourcing/CQRS application written in Haskell.
  9. Testing and Maintenance:

    • Describe best practices and tools for testing and maintaining such systems.
  10. Case Studies:

    • If possible, include real-world case studies or examples to showcase how these patterns have been effectively implemented in Haskell.

Visual Aids and Examples

  1. Include diagrams to visually represent architectural components and data flow.
  2. Provide full-fledged code examples, ideally as part of a running application, to demonstrate key concepts.

Dependency management features

  • freeze: Generate a lock file to freeze dependency versions
  • thaw: Update the lock file to new versions of dependencies

Implement `Channel` type

Reasoning

A Channel is a type often used in concurrent programming to handle inter-thread or inter-process communication. Channels are particularly useful for building complex workflows in a clean and modular way. Languages like Go have popularized the use of channels as a first-class concept for this purpose.

The main considerations for a Channel type in NeoHaskell are:

  1. Type Safety: Channels should be strongly-typed, ensuring that only the expected types of data can be sent and received.

  2. Synchronization: It's important to decide whether the channels are synchronous (blocking) or asynchronous (non-blocking). Some languages offer both.

  3. Buffering: Channels can be buffered or unbuffered, affecting how they behave when full or empty.

  4. Direction: In some languages, channels can be unidirectional (send-only or receive-only) or bidirectional. This can help enforce correct usage.

  5. Error Handling: Error conditions like closed channels or send/receive on a closed channel should be handled cleanly.

  6. Resource Management: Channels should automatically handle resource allocation and deallocation where applicable.

  7. Concurrency: Methods for safely using channels in a multi-threaded environment should be included.

  8. Interoperability: For maximum utility, the Channel type should easily interoperate with other types like Async, Option, and Result.

Functions for the Channel Module

Constructors

  • create: Create a new channel
  • buffered: Create a new buffered channel with a given capacity
  • unbuffered: Create a new unbuffered channel

Basic Operations

  • send: Send a value into the channel
  • receive: Receive a value from the channel
  • trySend: Try to send a value, returning immediately with a Result indicating success or failure
  • tryReceive: Try to receive a value, returning immediately with a Result indicating success or failure

Channel Properties

  • isClosed: Check if the channel is closed
  • isEmpty: Check if the channel is empty (for buffered channels)
  • isFull: Check if the channel is full (for buffered channels)

Synchronization

  • close: Close the channel, disallowing further sends
  • await: Block until the channel is closed or empty, useful for graceful shutdowns

Concurrency

  • select: Wait on multiple channel operations, executing the first one that becomes available
  • merge: Merge multiple channels into a single output channel

Error Handling

  • onError: Register a handler for error conditions (e.g., send on closed channel)

Interoperability

  • toAsync: Convert a channel receive operation into an Async
  • fromAsync: Convert an Async operation into a channel send operation

Debugging and Logging

  • toString: Convert the Channel to its string representation, mainly for debugging purposes

By offering a comprehensive API around the Channel type, NeoHaskell can enable powerful, flexible, and safe concurrent programming paradigms. As always, this is a starting point and the API can evolve based on real-world use cases and feedback.

Make the CLI support generating mobile apps

As per the tweet I made some time ago: https://twitter.com/NickSeagull/status/1700859705021571543

It is completely possible to create native mobile apps using NeoHaskell as the main codebase and a thin HyperView client that renders what NeoHaskell sends to it. (Caching the views so they have offline mode of some sorts)

The HyperView client part is an implementation detail that should be hidden from the users unless they really want to "eject" it and modify to their tastes (e.g. adding other native functionalities not handled by HyperView), but ideally in the future this client should be also configurable through NeoHaskell.

The CLI tool should be able to generate the client in an easy and transparent way for the users, as well as the APK, whatever iOS uses. Or even desktop apps, as React Native supports it.

Implement primitive types

Reasoning

The primitive types are the building blocks of any programming language. They are essential for creating more complex types and structures. While it's important to offer basic operations (e.g., addition, multiplication), we can also provide higher-level, more semantic functions that make the code more readable and expressive.

  1. Int and Float: In Ruby, for example, the numeric classes offer methods like even? and odd? which are more semantic than checking x % 2 == 0. These kinds of functions make the code more readable and self-explanatory.

  2. Bool: Functions that operate on booleans can offer logical manipulations that might not be immediately obvious, such as logical implication (implies) or exclusive or (xor).

  3. Char: Characters in languages like Python and Scala are usually equipped with a variety of utility methods, such as isDigit or isAlpha, that help in parsing or validating strings one character at a time.

By providing semantic and self-explanatory methods, we can make the standard library not only powerful but also accessible and easy to use. This is especially crucial for a language that aims to be beginner-friendly.

Functions for Each Primitive Module

Int

  • add: Adds two integers
  • subtract: Subtracts two integers
  • multiply: Multiplies two integers
  • divide: Divides two integers
  • mod: Modulus operation
  • isEven: Checks if the number is even
  • isOdd: Checks if the number is odd
  • abs: Absolute value
  • negate: Negation of the number
  • toFloat: Converts integer to floating-point number

Float

  • add: Adds two floating-point numbers
  • subtract: Subtracts two floating-point numbers
  • multiply: Multiplies two floating-point numbers
  • divide: Divides two floating-point numbers
  • floor: Rounds down to the nearest integer
  • ceil: Rounds up to the nearest integer
  • round: Rounds to the nearest integer
  • isInteger: Checks if the floating-point number can be represented as an integer
  • toInt: Converts to integer, if possible

Bool

  • !=: Instead of /=
  • and: Logical AND operation
  • or: Logical OR operation
  • not: Logical NOT operation
  • implies: Logical implication
  • xor: Exclusive OR
  • nand: Logical NAND
  • nor: Logical NOR

Char

  • isDigit: Checks if the character is a digit
  • isAlpha: Checks if the character is an alphabetic character
  • isLower: Checks if the character is a lowercase letter
  • isUpper: Checks if the character is an uppercase letter
  • toUpper: Converts the character to uppercase
  • toLower: Converts the character to lowercase
  • toInt: Converts the character to its ASCII integer representation
  • fromInt: Converts an ASCII integer to its character representation

These functions aim to offer a rich set of capabilities while keeping the API semantic and intuitive. As we proceed with implementation, we might discover the need for additional functions or modifications to existing ones based on real-world use cases and user feedback.

Implement Async type

Reasoning

Asynchronous programming is ubiquitous in modern software development, from I/O-bound tasks like reading files and making network requests to CPU-bound tasks like data processing. A type like Async encapsulates asynchronous computations, making it easier to reason about, compose, and manage them.

Languages like JavaScript with Promises, and Rust with its Future type, provide powerful abstractions for asynchronous programming. Here are some considerations for an Async type in NeoHaskell:

  1. Composability: The ability to chain or combine multiple asynchronous operations is crucial. Methods for sequencing and parallelizing tasks should be provided.

  2. Error Handling: Like Result, the Async type should make it easy to handle both successful and unsuccessful computations explicitly.

  3. Cancellation: In real-world scenarios, you often need to cancel an ongoing asynchronous operation. The API should offer a clean way to handle these situations.

  4. Resource Management: The Async type should make it easier to manage resources like files and network sockets that are inherently asynchronous.

  5. Interoperability: For maximum utility, the Async type should be easily convertible to and from other types like Option and Result.

Functions for the Async Module

Constructors

  • fromValue: Create an Async that immediately resolves with a value
  • fromError: Create an Async that immediately rejects with an error
  • fromCallback: Create an Async from a callback-based function

Basic Queries

  • isPending: Check if the Async operation is pending
  • isResolved: Check if the Async operation is resolved (completed successfully)
  • isRejected: Check if the Async operation is rejected (completed with an error)

Transformation and Composition

  • map: Transform the result of a successful Async operation
  • flatMap: Chain multiple Async operations together, sequencing them
  • recover: Handle the error in a rejected Async, potentially recovering with a new value
  • all: Combine multiple Async operations into one that resolves when all resolve
  • race: Combine multiple Async operations into one that resolves or rejects as soon as one of them does

Cancellation

  • cancel: Cancel the ongoing Async operation, if possible

Error Handling

  • handleError: Explicitly handle both resolved and rejected cases
  • orElse: Provide a fallback Async operation in case of rejection

Conversions

  • toResult: Convert the Async to a Result once it's settled, blocking if necessary
  • fromResult: Create an Async from a Result
  • toOption: Convert the Async to an Option once it's settled, blocking if necessary and discarding the error

Side Effects

  • tap: Perform a side-effect operation when the Async resolves, without changing its value
  • tapError: Perform a side-effect operation when the Async rejects, without changing its error

Debugging and Logging

  • toString: Convert the Async to its string representation, mainly for debugging purposes

By providing a rich set of operations around asynchronous programming, the Async type in NeoHaskell can offer both readability and robustness. As is the case with other types, this API can evolve based on user feedback and real-world use cases.

Define tasks for Lazy Evaluation docs

Reasoning

Lazy evaluation is a core concept in Haskell, and it's one that often confuses newcomers. Understanding lazy evaluation is crucial for writing efficient Haskell code and leveraging the language's full capabilities. A well-documented guide can help demystify this concept, making it more approachable for new and even experienced Haskell programmers.

Key considerations for creating documentation about Haskell's lazy evaluation:

  1. Accessibility: The documentation should be accessible to programmers of all levels, from beginners to advanced users.

  2. Clarity: The concept of lazy evaluation is inherently complex. Clear, concise language and good organization are vital to make the content understandable.

  3. Examples: Providing concrete examples can help clarify abstract concepts. These should cover common use-cases and edge-cases to give a comprehensive understanding.

  4. Comparisons: Comparing lazy evaluation to strict (eager) evaluation can help put the concept into context and make it easier to understand.

  5. Visual Aids: Graphs, flowcharts, and other visual aids can help explain how lazy evaluation works in practice.

  6. Performance Implications: Lazy evaluation has both pros and cons in terms of performance. It's essential to discuss these to give readers a balanced view.

  7. Common Pitfalls: Any documentation should aim to alert the reader to common mistakes and misconceptions related to lazy evaluation.

  8. Interactivity: Interactive code snippets, if possible, can help users experiment with lazy evaluation directly while reading the documentation, making the learning process more engaging.

  9. Glossary: Given that lazy evaluation uses specialized terminology (thunks, strictness, etc.), a glossary could help readers understand these terms better.

Tasks for "Create documentation that explains Haskell's Lazy Evaluation"

Planning and Structure

  1. Define the target audience and prerequisites for understanding the documentation.
  2. Decide on the documentation structure, including sections and subsections.
  3. Research existing literature and documentation on Haskell's lazy evaluation to identify gaps and opportunities for improvement.

Content Creation

  1. Write an introduction that outlines what lazy evaluation is and why it's important.
  2. Create a section comparing lazy and strict (eager) evaluation, explaining their differences and contexts where each is appropriate.
  3. Develop examples to showcase lazy evaluation in practice:
    • Basic examples showing lazy evaluation
    • Examples demonstrating the benefits of lazy evaluation
    • Examples illustrating common pitfalls
  4. Write about the technical details:
    • Explain how thunks work
    • Discuss evaluation strategies (e.g., call-by-need)
    • Describe how lazy evaluation interacts with IO, exceptions, and other language features
  5. Discuss the performance implications:
    • CPU and memory usage
    • Scenarios where lazy evaluation is beneficial
    • Scenarios where lazy evaluation could be problematic
  6. Identify and describe common pitfalls and how to avoid them.
  7. Create a glossary of terms related to lazy evaluation.

Visual Aids and Interactivity

  1. Create or source illustrations, flowcharts, or graphs to aid understanding.
  2. (Optional) Develop interactive code snippets that allow the reader to experiment with lazy evaluation.

Explain let — using do

We should probably promote using let inside of do blocks, regardless of context.

Less cognitive overhead of having to remember two different ways of declaring local variables.

Design tasks to document concurrency

Reasoning

Concurrency is an important aspect of modern software development, especially as we transition towards more distributed and parallel architectures. Haskell offers various tools and strategies for dealing with concurrency, each with its unique advantages and trade-offs. However, concurrency models in Haskell can be challenging to grasp for newcomers and even experienced developers if not well-documented.

Here are some considerations for documenting Haskell's concurrency tools and strategies:

  1. Scope: Define what aspects of concurrency will be covered. Haskell offers multiple models such as Threads, Software Transactional Memory (STM), Actors (using libraries), etc.

  2. Target Audience: Identify the expected user base, which can range from beginners who have never dealt with concurrency to advanced users looking for Haskell-specific optimizations.

  3. Real-world Applications: Use real-world examples or case studies to illustrate how certain tools and strategies can be effectively applied.

  4. Performance: Discuss the performance implications of different concurrency models and how to measure and optimize them.

  5. Troubleshooting: Provide solutions to common problems or bugs one might encounter when dealing with concurrency in Haskell.

  6. Safety and Pitfalls: Highlight safety features or practices in Haskell for concurrency and also discuss common pitfalls.

  7. Terminology: Concurrency comes with its jargon, which should be clearly defined for readers.

  8. Compatibility: Discuss how these concurrency models interact with other Haskell features and external systems.

  9. Future Developments: Acknowledge any upcoming features or community-driven projects that might affect Haskell's concurrency landscape.

Tasks for "Document Haskell Concurrency tools and strategies"

Planning and Structure

  1. Outline the scope of the documentation, specifying which tools and strategies will be covered.
  2. Identify the target audience and set the documentation tone accordingly.
  3. Establish the structure, including major sections and subsections.

Content Creation

  1. Introduction:

    • Explain what concurrency is and why it's important in Haskell.
  2. Basic Concurrency Models:

    • Threads
    • Fork/Join model
    • Parallelism vs Concurrency
  3. Advanced Concurrency Models:

    • Software Transactional Memory (STM)
    • Asynchronous programming (Async library)
  4. Real-world Examples:

    • Provide practical examples illustrating different concurrency models.
  5. Performance Implications:

    • Discuss how different models impact performance.
    • Provide guidelines on performance measurement and optimization.
  6. Safety and Best Practices:

    • Detail Haskell-specific features that make concurrent programming safer.
    • Discuss best practices and patterns for writing concurrent Haskell code.
  7. Troubleshooting and Common Pitfalls:

    • Explain common problems users may encounter and how to troubleshoot them.
  8. Terminology Glossary:

    • Define key terms related to concurrency.

Visual Aids and Examples

  1. Develop diagrams, flowcharts, or tables to illustrate complex concepts and workflows.
  2. Include code examples, preferably with explanations and maybe even interactive components where readers can tweak parameters to see different outcomes.

Implement Result type

Reasoning

The Result type is a robust and expressive way to handle errors in many programming languages, especially those with strong influences from functional programming, such as Rust and Elm. Result is typically a tagged union comprising two cases: Ok for successful computations and Err for errors. Unlike Option, which only indicates the presence or absence of a value, Result can carry information about the error, making it well-suited for functions that can fail in multiple ways. Key considerations include:

  1. Explicit Error Handling: The Result type forces developers to explicitly handle both successful and error cases, reducing the likelihood of unhandled errors.

  2. Type Safety: The error type is part of the Result's type definition, making it easier to understand what kinds of errors a function can produce.

  3. Composability: Like Option, Result should have higher-order functions that allow it to be easily transformed, chained, and incorporated into larger data pipelines.

  4. Integration with Other Types: It's often useful to convert between Result and other types like Option or even native exceptions, depending on the programming paradigm used in the codebase.

Functions for the Result Module

Constructors

  • ok: Create a Result representing a successful computation (Ok)
  • err: Create a Result representing an error (Err)

Basic Queries

  • isOk: Check if the Result is an Ok
  • isErr: Check if the Result is an Err
  • unwrap: Retrieve the value if Ok, or panic (throw an exception) if Err
  • unwrapOr: Retrieve the value if Ok, or return a default value if Err

Transformations

  • map: Apply a function to the value inside Ok
  • mapErr: Apply a function to the error inside Err
  • flatMap: Apply a function that returns a Result to the value inside Ok

Error Handling and Recovery

  • orElse: Provide a fallback Result in case of an Err
  • match: Pattern match on the Result to handle both Ok and Err cases explicitly

Conversions

  • toOption: Convert the Result to an Option, discarding the error information
  • fromOption: Create a Result from an Option, with a default error value for None

Side Effects

  • forEach: Execute a side-effect function on the value if Ok
  • forEachErr: Execute a side-effect function on the error if Err

Inspection and Debugging

  • toString: Convert the Result to its string representation for debugging

By providing a comprehensive set of functions for error handling and transformation, the Result type can be a cornerstone of robust and maintainable code in NeoHaskell. The API is intended to be beginner-friendly while still providing the power features that more advanced users would expect. As with other types, user feedback and real-world usage should inform future iterations of the API.

Attempt reviving the CPython library

The CPython library is pretty much dead, but it allows calling Python code from Haskell, as well as marshalling values back and forth.

We should attempt reviving it, as it should be a matter of adapting the FFI to the latest Python version ABI.

Implement Option type

Reasoning

The Option type is a staple in functional programming languages and has found its way into more mainstream languages as well, such as Swift's Optional and Kotlin's Optional. It is used to represent a value that can either be present (Some) or absent (None). The Option type is commonly used for:

  1. Null Safety: One of the primary uses is to safely encapsulate the absence of a value, as opposed to using null or nil, which can lead to runtime errors.

  2. Function Return Types: It's often used as the return type of functions that may or may not produce a value. For example, a function that searches for an element in a collection can return an Option to signify whether the search was successful.

  3. Configuration: It's useful for optional configuration settings that have sensible defaults.

  4. Pipeline Processing: Higher-order functions like map, filter, and flatMap can be defined on an Option, making it composable in data pipelines.

  5. Error Handling: While Option is not a substitute for proper error types like Either or Result, it can be used for simple error handling scenarios where the only important information is the presence or absence of a value.

Incorporating these considerations, let's define the API for the Option type.

Functions for the Option Module

Constructors

  • some: Create an Option with a value (Some)
  • none: Create an empty Option (None)
  • fromNullable: Create an Option from a value that could be null

Basic Queries

  • isEmpty: Check if the Option is None
  • isSome: Check if the Option is Some
  • getOrElse: Retrieve the value or a default if the Option is None

Transformations

  • map: Apply a function to the inner value of the Option if it's Some
  • flatMap: Apply a function that returns an Option to the inner value of the Option if it's Some

Filtering

  • filter: Apply a predicate function to the Option and turn it into None if the predicate fails

Side Effects

  • forEach: Execute a side-effect function if the Option is Some

Error Handling

  • orElse: Chain multiple Options together, returning the first Some found
  • getOrElseThrow: Retrieve the value or throw an exception if the Option is None

Conversions

  • toList: Convert the Option to a list containing zero or one element
  • toString: Convert the Option to its string representation

By adopting a rich and expressive set of operations, the Option type can serve as a powerful tool for safe and expressive programming in NeoHaskell. As always, the API can be expanded or refined based on user feedback and real-world use cases.

Project Management features

  • init: Initialize a new NeoHaskell project with a template
  • build: Compile the project
  • run: Run the project
  • clean: Clean build artifacts

Implement Text

Reasoning

String manipulation is one of the most common tasks in software development. Whether it's for parsing input, generating output, or implementing complex text transformations, a robust and intuitive Text API is essential. A good Text module should make these tasks easy and efficient, with a focus on readability and expressiveness.

Languages like Python and Ruby provide an extensive library for string manipulations, offering both fundamental and high-level, semantic methods. This makes it easy to work with strings for a variety of tasks, ranging from simple to complex.

Some important considerations include:

  1. Substrings: Operations to extract a substring are foundational and should be included.

  2. Concatenation and Interpolation: Joining strings is extremely common, and the API should offer several ways to accomplish this.

  3. Case Manipulation: Changing the case of a string or checking its case is frequently needed.

  4. Trimming: Removing whitespace from the beginning and end of a string is another common requirement.

  5. Search and Replace: Whether it's finding a simple substring or doing complex pattern matching, search and replace are critical functionalities.

  6. Encoding and Decoding: Sometimes, we need to convert strings to different formats or encodings.

Functions for the Text Module

Basic Operations

  • length: Return the length of the string
  • isEmpty: Check if the string is empty
  • charAt: Get the character at a specific index
  • concat: Concatenate two strings

Substring and Slicing

  • substring: Extract a substring from a string by specifying start and end indices
  • slice: Similar to substring but supports negative indices to count from the end
  • split: Split the string into a list based on a separator

Case Manipulation

  • toLowerCase: Convert all characters in the string to lowercase
  • toUpperCase: Convert all characters in the string to uppercase
  • capitalize: Capitalize the first letter of the string

Trimming

  • trim: Remove whitespace from both ends of the string
  • trimStart: Remove whitespace from the beginning of the string
  • trimEnd: Remove whitespace from the end of the string

Search and Replace

  • indexOf: Find the index of the first occurrence of a substring
  • lastIndexOf: Find the index of the last occurrence of a substring
  • contains: Check if the string contains a certain substring
  • replace: Replace all occurrences of a substring with another string
  • replaceAll: Replace based on a regular expression

Encoding and Decoding

  • encode: Encode the string into a certain format (e.g., Base64)
  • decode: Decode the string from a certain format (e.g., Base64)

Others

  • startsWith: Check if the string starts with a certain prefix
  • endsWith: Check if the string ends with a certain suffix
  • reverse: Reverse the string

By offering both low-level and high-level methods, the Text module can be a powerful tool for developers working in NeoHaskell. Like with other modules, the list of functions may evolve based on user feedback and specific needs.

Implement basic Collections

Reasoning

Collections are the workhorses of any programming language. They are ubiquitous in codebases for a good reason: they provide ways to group, sort, filter, and transform data. Given their importance, they should be both performant and pleasant to work with.

  1. List: Many languages like Python, Ruby, and Scala provide a broad set of functionalities for lists, arrays, or equivalent structures, including functions for filtering, slicing, and mapping. Making these functions semantic helps with readability. For example, methods like first and last are more readable than using index-based access for the first and last elements.

  2. Map: In Python, dictionaries offer not only the typical get/set functionality but also more semantic methods like getOrDefault and keys. These kinds of functions can greatly simplify code and reduce the risk of errors.

  3. Set: The power of a set lies in its ability to manage an unordered collection of unique elements. Therefore, beyond basic CRUD operations, sets often include semantic set operations like union, intersection, and difference, making operations on sets very readable.

  4. Tuple: Tuples are often immutable and fixed in size. They are perfect for grouping several elements into a single item. Although they are simple in nature, a few utility functions can make working with them easier, like functions to access elements by position.

Here's the breakdown of functions that should be defined in each module.

Functions for Each Collections Module

List

  • includes: Instead of elem
  • append: Add an element to the end
  • prepend: Add an element to the beginning
  • insertAt: Insert an element at a specific index
  • removeAt: Remove the element at a specific index
  • length: Get the length of the list
  • isEmpty: Check if the list is empty
  • first: Get the first element
  • last: Get the last element
  • slice: Get a sub-list from the list
  • map: Apply a function to all elements
  • takeIf: Filter elements based on a predicate (alternative to filter)
  • dropIf: same as above but negated
  • reduce: Reduce the list to a single value
  • infinitely: Create an infinite list of the element (alternative to repeat)

Map

  • Some kind of operator to create a map easily from a list (like in Ruby, Elixir, Java, etc)
  • put: Add a key-value pair
  • get: Retrieve a value by key
  • remove: Remove a key-value pair by key
  • containsKey: Check if a key exists
  • keys: Retrieve all keys
  • values: Retrieve all values
  • isEmpty: Check if the map is empty
  • size: Get the size of the map
  • getOrDefault: Get the value for a key, or return a default value if the key does not exist

Set

  • add: Add an element
  • remove: Remove an element
  • contains: Check if an element exists
  • isEmpty: Check if the set is empty
  • size: Get the size of the set
  • union: Return a new set that is the union of two sets
  • intersection: Return a new set that is the intersection of two sets
  • difference: Return a new set that is the difference between two sets

Tuple

  • create: Create a new tuple
  • getAt: Get element at specific index
  • size: Get the size of the tuple
  • toArray: Convert the tuple to an array
  • fromArray: Create a tuple from an array

The aim is to provide a comprehensive, yet straightforward and semantic API for each type of collection. The API should be intuitive enough for beginners while also offering the depth needed by more advanced users. Like with primitives, we might need to make adjustments or add more functions as we receive feedback and analyze real-world usage.

Implement `DateTime`

Reasoning

Date and time manipulation is a common requirement in many kinds of applications, from simple logging to complex scheduling and time-series analysis. However, it's also an area fraught with complexities due to time zones, leap years, daylight saving time, and other factors. Languages like Python provide good support for date and time manipulation via their standard libraries, whereas others like Java have seen significant improvements with the introduction of the Java 8 Date/Time API.

Key considerations for the DateTime type in NeoHaskell are:

  1. Immutability: Date and time instances should be immutable to prevent unexpected side-effects.

  2. Precision: It should be possible to represent dates and times with varying degrees of precision (years to nanoseconds).

  3. Time Zones: Time zone support should be robust, yet simple to use.

  4. Calculations: Basic calculations like addition, subtraction, and comparisons should be straightforward.

  5. Formatting and Parsing: Converting between DateTime and string should be simple and support common formats.

  6. Interoperability: Should interoperate well with other types like Duration.

  7. Standardization: Adherence to widely accepted date and time standards, such as ISO 8601, is crucial.

Functions for the DateTime Module

Constructors

  • now: Create a new DateTime instance representing the current date and time
  • fromString: Create a DateTime from a string with a specific format
  • fromTimestamp: Create a DateTime from a Unix timestamp

Queries

  • year: Get the year
  • month: Get the month
  • day: Get the day
  • hour: Get the hour
  • minute: Get the minute
  • second: Get the second
  • millisecond: Get the millisecond
  • nanosecond: Get the nanosecond

Time Zone

  • timezone: Get the time zone
  • withTimezone: Create a new DateTime with a different time zone
  • utc: Convert the DateTime to Coordinated Universal Time (UTC)

Calculations

  • addDuration: Add a duration
  • subtractDuration: Subtract a duration
  • difference: Calculate the difference between two DateTime instances
  • isBefore: Check if the DateTime is before another
  • isAfter: Check if the DateTime is after another
  • isEqualTo: Check if two DateTime instances represent the same point in time

Formatting and Parsing

  • toString: Convert the DateTime to a string with a specific format
  • fromString: Parse a DateTime from a string with a specific format

Interoperability

  • toTimestamp: Convert the DateTime to a Unix timestamp
  • fromDuration: Create a DateTime by adding a Duration to a base DateTime

Special Cases

  • isLeapYear: Check if the year is a leap year
  • weekday: Get the weekday
  • startOfDay: Get the DateTime representing the start of the day
  • endOfDay: Get the DateTime representing the end of the day

Debugging and Inspection

  • toString: Convert the DateTime to its string representation, primarily for debugging purposes

By offering a comprehensive set of operations around the DateTime type, NeoHaskell can significantly simplify working with dates and times, making the code more expressive and less prone to errors. This API can evolve based on community feedback and real-world usage.

Implement `File` operations

Reasoning

File I/O is a fundamental aspect of many programming tasks, whether you're reading configuration data, writing logs, or manipulating larger data sets. However, file operations can be cumbersome, error-prone, and insecure if not done correctly. An elegant standard library should offer a File type that makes file operations straightforward, safe, and efficient. In languages like Python, file handling is incredibly simple, while in languages like Rust, it's incredibly robust. Here are some key considerations:

  1. Safety: File operations should not crash the program or corrupt data. Any kind of failure, like not being able to find the file or lacking permissions to read/write, should be handled gracefully.

  2. Convenience: Common operations like reading all lines in a file or dumping a string into a file should be one-liners.

  3. Flexibility: For more complex scenarios, it should be possible to read and write data in chunks, seek to specific positions, etc.

  4. Resource Management: File handles should be automatically managed, so they are closed when no longer needed, to prevent resource leaks.

  5. Error Handling: Errors should be expressive enough to indicate the kind of failure (e.g., file not found, permission denied, etc.)

  6. Encoding and Formatting: There should be simple ways to handle different text encodings and file formats.

  7. Interoperability: The File type should be easily convertible to and from types like Stream, String, and Bytes.

Functions for the File Module

Constructors and Opening Methods

  • open: Open a file with specified mode (read, write, append, etc.)
  • create: Create a new file, failing if it already exists
  • temp: Create a new temporary file

Basic Reading and Writing

  • readAll: Read the entire content into memory (caution for large files)
  • writeAll: Write the entire content from memory
  • readLine: Read a single line from the file
  • writeLine: Write a single line into the file

Chunked Operations

  • readChunk: Read a chunk of data of a specified size
  • writeChunk: Write a chunk of data

File Positioning

  • seek: Move the file pointer to a specific position
  • tell: Tell the current position of the file pointer

File Properties

  • size: Get the size of the file
  • exists: Check if the file exists
  • isReadable: Check if the file is readable
  • isWritable: Check if the file is writable

File Manipulation

  • rename: Rename the file
  • delete: Delete the file
  • copy: Copy the file to another location

Resource Management

  • close: Explicitly close the file handle
  • withFile: Run a block of code with a file handle, ensuring it is closed afterwards

Error Handling

  • lastError: Retrieve the last error that occurred during a file operation

Encoding and Formatting

  • setEncoding: Set the text encoding for the file
  • getEncoding: Get the current text encoding

Interoperability

  • toStream: Convert the file to a read/write data stream
  • fromStream: Create a file from a data stream

Debugging and Inspection

  • toString: Convert the file metadata to its string representation for debugging

By providing a well-thought-out API around file operations, NeoHaskell can make it easy for both beginners and experts to perform safe and efficient file I/O. As usual, this API would evolve based on user feedback and real-world usage.

A Note on GitHub Issues and Task Planning: Flexibility and Alignment with Project Philosophy

The GitHub issues you see outlining various planning tasks are designed to serve as a foundational roadmap for the directions and initiatives we want to pursue in this project. However, it's essential to understand that these issues are more of a starting point rather than a rigid set of instructions. They are a rough draft intended to guide our collective efforts but are by no means set in stone.

Prioritizing Developer Happiness

NeoHaskell is committed to optimizing developer happiness. This philosophy should be at the forefront of every task and issue we tackle. Before jumping into the coding or documentation work specified in a GitHub issue, take a moment to consider the following:

  • How does this task contribute to a more enjoyable or efficient development experience for contributors?
  • Are there alternative approaches that might align better with our goal of optimizing developer happiness?
  • How does this task fit into the larger project roadmap and long-term vision?

Team Communication Through GitHub and Discord

Before you start actively working on an issue, it's a good practice to communicate your intentions and thoughts with the team. You can do this by commenting on the GitHub issue itself or by reaching out on the Discord server. Doing so offers several advantages:

  • It gives team members an opportunity to provide input or insights that might not have been considered.
  • It helps in recognizing challenges or complications early on, allowing for a smoother development process.
  • It ensures that everyone is aligned with the task's objectives and the project's overarching philosophy.

Remember, the objective is not just to complete tasks but to do so in a manner that aligns with our mission of enhancing the developer experience.

Thank you for contributing to this mission!

Testing features

  • test: Run tests
  • test-watch: Run tests in watch mode, rerunning on file changes

Utility features

  • generate: Generate boilerplate code, e.g., for a new module or function
  • docs: Generate or serve project documentation
  • info: Show system and project info relevant for debugging
  • help: Provide help information for the CLI commands

Deployment features

  • publish: Publish a package to a registry
  • deploy: Deploy a project to a specified environment
  • check-versioning: Checks the exposed data types/functions/etc... and ensures that the version bump was properly done

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.