### PureScript: Demonstrate Logger Testing with Writer Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This PureScript example demonstrates how to use the `logMessageWriter` function to test a sequence of log messages at different levels. It simulates logging operations at a fixed timestamp and collects the output in a `Writer` monad. This allows developers to assert the correctness of the log content and order in a pure, reproducible manner. ```PureScript logMessages :: Writer (Array String) Unit logMessages = logMessage' Debug "Preparing to start application" >>= logMessage' Debug "Setting environment log level to debug" >>= logMessage' Info "Starting up application" >>= logMessage' Error "Failed to start up application" where -- Log messages on January 1, 2018 at midnight logMessage' = logMessageWriter (unsafeMkDateTime 2018 1 1 0 0 0 0) ``` -------------------------------- ### Add Capabilities to a Halogen Component Source: https://thomashoneyman.com/guides/real-world-halogen/using-halogen-components This example demonstrates how to add a capability, such as 'LocalStorage m', to a Halogen component's type signature. This allows the component to access pure functions defined within that capability, integrating them into its behaviors. ```PureScript myComponent :: forall m. LocalStorage m => Component HTML Query Input Output m ``` -------------------------------- ### Example JSON for User Profile Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This JSON object illustrates the server's response format for a user profile. It includes fields such as username, bio, image URL, and a boolean indicating if the current user is following this profile. ```JSON { "profile": { "username": "jake", "bio": "I work at statefarm", "image": "https://static.productionready.io/images/smiley-cyrus.jpg", "following": false } } ``` -------------------------------- ### Example JSON for Authenticated User Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This JSON object demonstrates the server's response for an authenticated user. It extends the profile data with email and a JWT token, while omitting the 'following' status, reflecting the current user's own data. ```JSON { "user": { "email": "jake@jake.jake", "token": "jwt.token.here", "username": "jake", "bio": "I work at statefarm", "image": null } } ``` -------------------------------- ### PureScript: Defining Routes with Custom Data Types (Recommended) Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This example demonstrates how to define application routes using a custom `data` type in PureScript. This approach significantly improves type safety by restricting possible route values and makes function signatures more descriptive, preventing invalid route inputs. ```PureScript data Route = Home | Settings navigate :: Route -> Effect Unit navigate = case _ of Home -> setHashTo "home" Settings -> setHashTo "settings" ``` -------------------------------- ### Implement Pure Purescript LogMessage Formatting Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet defines the core pure logic for formatting and processing log messages. It introduces a `newtype LogMessage` for formatted strings and the `logMessage` function. This function takes a monadic action to get the current time, a function to write the formatted log, the `LogType`, and the raw message, then formats the message with a timestamp and level prefix before writing it. This design ensures the core logic is testable and abstract. ```Purescript -- We aren't logging an arbitrary string anymore -- we've now -- formatted it according to particular rules. newtype LogMessage = LogMessage String logMessage :: forall m . Monad m => m DateTime -- | How should we fetch the current time? -> (LogMessage -> m Unit) -- | How should we write this message? -> LogType -- | What kind of log is this? -> String -- | What is the input string? -> m Unit logMessage getTime writeLog logType msg = do t <- fmtDateTime <$> getTime let msg' = LogMessage $ case logType of Debug -> "[DEBUG: " <> t <> "]\n" <> msg Info -> "[INFO: " <> t <> "]\n" <> msg Error -> "[ERROR: " <> t <> "]\n" <> msg writeLog msg' ``` -------------------------------- ### Implement Component State with RemoteData in Purescript Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This example demonstrates how to refactor the component's initial state to leverage the `RemoteData` type. By encapsulating the data fetching status within a single type, it ensures that the component state is always consistent and accurately reflects whether data has been requested, is loading, has failed, or has successfully loaded. ```Purescript type ComponentState = { stuff :: RemoteData Error (Array Stuff) } myInitialState :: ComponentState myInitialState = { stuff: NotAsked } ``` -------------------------------- ### PureScript/Haskell: Enforce Non-Empty Collections in Types Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This example illustrates how to use a 'NonEmptyArray' type to ensure that an 'Order' always contains at least one item. By preventing the construction of an empty order, functions operating on the 'Order' type no longer need to handle the failure case of an empty item list, simplifying their signatures and reducing error propagation. ```PureScript type Order = { items :: Array Item } -- We can encode the possibility of failure with Maybe, but -- every function operating on the type now has to result in -- additional cases, which will then have to be handled by the -- caller of the function processOrder :: Order -> Maybe Order type Order = { items :: NonEmptyArray Item } processOrder :: Order -> Order splitOrder :: Order -> NonEmptyArray Order ``` -------------------------------- ### Implement PureScript runAppM Transformation Function Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet provides the `runAppM` function, which serves as the entry point to execute computations within the `AppM` monad. It takes an `Env` value and an `AppM` computation, transforming it into an `Aff` computation by running the underlying `ReaderT` transformer with the provided environment. ```PureScript runAppM :: Env -> AppM ~> Aff runAppM e (AppM m) = runReaderT m e ``` -------------------------------- ### Implement PureScript MonadAsk Instance for AppM Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet implements the `MonadAsk` instance for `AppM`, enabling computations within `AppM` to access the global `Env` environment. It uses `Type.Equality` to correctly handle `Env` as a type synonym, allowing the `ask` function to retrieve the environment from the `ReaderT` context. ```PureScript import Type.Equality as TE instance monadAskAppM :: TE.TypeEquals e Env => MonadAsk e AppM where ask = AppM $ asks TE.from ``` -------------------------------- ### PureScript: Initial Follow Function Signature Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This is an initial attempt at defining a `follow` function. It takes a `UserProfile` to follow and an `AuthUser` for authentication, returning an `Effect Unit`. This signature is later refined to enforce stricter type constraints, preventing invalid follow operations. ```PureScript -- We can't just use a `Token` directly because it is only accessible -- via the AuthUser type. Later, we'll examine ways to make even this -- a pure function! follow :: UserProfile -> AuthUser -> Effect Unit ``` -------------------------------- ### Create a Pure Halogen Render Function Source: https://thomashoneyman.com/guides/real-world-halogen/using-halogen-components This snippet illustrates a pure render function in Halogen, which takes a 'Profile' and produces 'HTML'. Such functions are ideal for common view parts like headers or footers, promoting reusability and purity within components. ```PureScript -- Given an user profile, render a header with their username (for example) renderHeader :: forall p i. Profile -> HTML p i ``` -------------------------------- ### Implement Purescript LogMessages for HalogenM Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet provides an instance of the `LogMessages` type class for `HalogenM`, the Halogen monad. It allows `LogMessages` functions to be used directly within Halogen components without explicit lifting, provided the underlying monad `m` also has a `LogMessages` instance. This boilerplate simplifies the use of logging within Halogen applications. ```Purescript instance LogMessagesHalogenM :: LogMessages m => LogMessages (HalogenM state action slots output m) where log l s = lift (log l s) ``` -------------------------------- ### PureScript: Implement AppM Logger Instance Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This PureScript code defines an instance of `LogMessages` for the `AppM` monad. It conditionally logs messages based on the `logLevel` in the environment, directing `Debug` and `Warn` messages to the console in `Testing` mode and `Error` messages to an external service in `Production` mode. It leverages `MonadAsk`, `Effect`, and `Aff` for environment access and side effects. ```PureScript -- Our instance instance logMessagesAppM :: LogMessages AppM where log logType str = do env <- ask let shouldOutput | env.logLevel == Production && logType == Error = true | env.logLevel == Testing = true | otherwise = false writeLog :: Message -> AppM Unit writeLog msg = do case env.logLevel of Testing -> liftEffect $ Console.log $ unwrap msg Production -> liftAff $ sendToExternalService msg when shouldOutput do logMessage (liftEffect nowDateTime) writeLog logType str ``` -------------------------------- ### Define PureScript Public Resource Access Capability Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This PureScript type class, `GetPublicResource`, defines the interface for accessing public resources without requiring authentication. It specifies functions like `getArticle`, `getUserProfile`, and `getArticlesByTag`, each returning an `Either Error` type to handle potential failures. This capability allows for testing and running functions independently of specific user credentials or backend implementations. ```APIDOC -- This class represents the ability to acquire various resources -- without authentication class Monad m <= GetPublicResource m where getArticle :: Slug -> m (Either Error Article) getUserProfile :: Username -> m (Either Error UserProfile) getArticlesByTag :: Tag -> m (Either Error (Array Article)) ``` -------------------------------- ### Implementing a Smart Constructor for Username in Haskell Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This Haskell code demonstrates the 'smart constructor' pattern to create a 'Username' type that guarantees its internal string meets specific validation rules (5-50 alphanumeric characters). The data constructor is hidden, forcing users to use 'mkUsername' for validated instantiation, thereby ensuring type safety. ```Haskell -- We are only exporting the Username *type*, but we aren't exporting -- its *constructor*. Other modules can import the type, but you can't -- actually construct a value directly. -- -- To export the type AND its constructors, use `Username(..)` module MyModule (Username, mkUsername) where -- We will restrict this type to represent strings between 5 and -- 50 alphanumeric characters newtype Username = Username String -- This function will be the only way to construct a `Username`, -- and it will ensure this guarantee is met. mkUsername :: String -> Maybe Username mkUsername = Username <=< inRange 5 50 <=< allAlphaNum where inRange :: Int -> Int -> String -> Maybe String allAlphaNum :: String -> Maybe String ``` -------------------------------- ### Define PureScript Authenticated Resource Capabilities Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges These PureScript type classes, `GetAuthResource` and `ManageAuthResource`, define capabilities for interacting with authenticated resources. `GetAuthResource` provides read-only access to authenticated data, while `ManageAuthResource` extends it with write operations like updating user profiles or managing articles. Both rely on the `Authentication` class, ensuring all operations are tied to a user's credentials and promoting clear separation of read and write concerns. ```APIDOC -- This class represents the ability to acquire resources that require -- authentication. Because it relies on our `Authentication` class, we -- can use those functions in our implementation. class Authentication m <= GetAuthResource m getAuthUserProfile :: m (Either Error UserProfile) getArticleFeed :: m (Either Error (Array Article)) -- This class represents the ability to update resources in the system, -- all of which will require authentication. It's separated because we -- can then make it clear which functions have read-only access and -- which have read & write access. class GetAuthResource m <= ManageAuthResource m where updateUser :: UserProfile -> m (Either Error UserProfile) followUser :: FollowedAuthor -> m (Either Error UserProfile) unfollowUser :: UnfollowedAuthor -> m (Either Error UserProfile) deleteArticle :: Slug -> m (Either Error Unit) ``` -------------------------------- ### Define Initial Component State with Array in Purescript Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet illustrates an initial attempt to model component state using an empty array for data. This approach creates ambiguity, as an empty array doesn't differentiate between data not yet fetched, no data available, or an error during fetching. ```Purescript type ComponentState = { stuff :: Array Stuff } myInitialState :: ComponentState myInitialState = { stuff: [] } ``` -------------------------------- ### Define Purescript LogMessages Type Class Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet defines the `LogMessages` type class, which abstracts the ability of a monad `m` to log messages. It requires `m` to be an instance of `Monad` and introduces a single function, `log`, which takes a `LogType` and a `String` message, returning `m Unit`. This class provides a pure interface for logging, allowing different implementations based on the monad. ```Purescript -- We will ultimately want these functions to run in a monad which -- can support effects. class Monad m <= LogMessages m where log :: LogType -> String -> m Unit ``` -------------------------------- ### Derive Core Monad Instances for PureScript AppM Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet leverages PureScript's `derive newtype` mechanism to automatically generate essential type class instances for `AppM`. By deriving `Newtype`, `Functor`, `Apply`, `Applicative`, `Bind`, and `Monad`, `AppM` gains the fundamental behaviors required for a functional monad, allowing for sequential and conditional computations. ```PureScript derive instance newtypeAppM :: Newtype (AppM a) _ derive newtype instance functorAppM :: Functor AppM derive newtype instance applyAppM :: Apply AppM derive newtype instance applicativeAppM :: Applicative AppM derive newtype instance bindAppM :: Bind AppM derive newtype instance monadAppM :: Monad AppM ``` -------------------------------- ### PureScript: Representing Routes with Primitive String Types (Less Ideal) Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet shows a less ideal way to represent application routes using a simple `String` type. While functional, it lacks type safety and expressiveness, as `String` can represent many values that are not valid routes, making type signatures less informative. ```PureScript home :: String home = "home" -- A function which will set the hash, triggering a route change navigate :: String -> Effect Unit navigate r = setHashTo r ``` -------------------------------- ### PureScript: Create Test Logger with Writer Monad Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This PureScript function creates a testable logging mechanism using the `Writer` monad, allowing for verification of log formatting and content without actual side effects. It records log messages as an array of strings, making it suitable for unit testing the logger's logic. This approach decouples the logging capability from its concrete implementation. ```PureScript logMessageWriter :: DateTime -> LogType -> String -> Writer (Array String) Unit logMessageWriter dt logType str = logMessage getTime writeMsg logType str where getTime = const dt writeMsg = tell <<< pure ``` -------------------------------- ### Define PureScript Email Newtype and Smart Constructor Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This PureScript snippet defines a `newtype` for `Email` to enforce type safety for email addresses. It includes a `mkEmail` smart constructor, which allows for controlled creation of `Email` values, ensuring validation before construction. ```PureScript newtype Email = Email String mkEmail :: String -> Maybe Email ``` -------------------------------- ### Define PureScript AppM Monad Newtype Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet defines the `AppM` newtype, which is the core of the custom monad. It wraps `ReaderT Env Aff a`, indicating that `AppM` is a ReaderT transformer over an `Aff` base monad, providing access to the `Env` environment and supporting asynchronous effects. ```PureScript newtype AppM a = AppM (ReaderT Env Aff a) ``` -------------------------------- ### Derive PureScript MonadEffect and MonadAff Instances for AppM Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet derives `MonadEffect` and `MonadAff` instances for the `AppM` monad. These instances are crucial for `AppM` to interact with side effects (`Effect`) and handle asynchronous computations (`Aff`), leveraging the capabilities of its underlying `Aff` base monad. ```PureScript derive newtype instance monadEffectAppM :: MonadEffect AppM derive newtype instance monadAffAppM :: MonadAff AppM ``` -------------------------------- ### Enhance Component State with Flags in Purescript Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This code shows an improved component state model that includes `errors` and `fetched` flags to track data status. While addressing some ambiguities, this model introduces new problems, such as the possibility of inconsistent states (e.g., having both data and errors) and the need to manually synchronize flags. ```Purescript type ComponentState = { stuff :: Array Stuff , errors :: Error , fetched :: Boolean } ``` -------------------------------- ### PureScript ProfilePhoto Type Definition Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This code defines the `ProfilePhoto` type in PureScript for user profile images. It uses a newtype over `NonEmptyString` to ensure the URL is not empty, and wraps it in `Maybe` to represent that a profile photo might be optional. This approach clarifies the intended use of the string as an image location. ```PureScript newtype ProfilePhoto = ProfilePhoto NonEmptyString { ... , image :: Maybe ProfilePhoto } ``` -------------------------------- ### Define PureScript LogLevel and Environment Types Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet defines a `LogLevel` data type to represent different logging verbosity levels (Testing, Production) and an `Env` type synonym. The `Env` type serves as the application's read-only global environment, containing the `logLevel` that will be accessible within the custom monad. ```PureScript data LogLevel = Testing | Production type Env = { logLevel :: LogLevel } ``` -------------------------------- ### PureScript Username Type Definition Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This code defines two options for representing a `Username` type in PureScript. Option A uses a newtype over `String` with a smart constructor `mkUsername` to ensure non-empty values. Option B uses a newtype over `NonEmptyString` for guaranteed non-emptiness. Both approaches treat server-provided empty strings as errors. ```PureScript -- option A newtype Username = Username String mkUsername :: String -> Maybe Username mkUsername "" = Nothing mkUsername s = Just (Username s) -- option B newtype Username = Username NonEmptyString ``` -------------------------------- ### Define PureScript AuthUser Module with Hidden Constructor Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This PureScript module defines the `AuthUser` type and hides its data constructor to restrict direct access to the authentication token. The `toLocalStorage` function demonstrates how the token can be used internally within the module while remaining inaccessible externally, providing controlled side effects. ```PureScript module AuthUser (AuthUser, toLocalStorage, ...) where -- Usually I wouldn't reach for a type alias, but the token is not -- being exported from this module. type Token = String -- We aren't sure what additional data to store beyond the token just -- yet, but we'll fix this later. data AuthUser = AuthUser Token ??? -- We can still freely use the token within this module, though it -- will be inaccessible outside the module. toLocalStorage :: AuthUser -> Effect Unit toLocalStorage (AuthUser tok _) = do ... ``` -------------------------------- ### Define PureScript UserProfile from Extensible Fields Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This PureScript type alias demonstrates how to recover the original `UserProfile` type by instantiating `UserProfileFields` with an empty row. This showcases the flexibility of extensible records, allowing a base type to be defined without additional fields. ```PureScript -- We can recover our original record by simply not providing any -- additional fields. type UserProfile = UserProfileFields () ``` -------------------------------- ### Define a Pure Halogen Component Source: https://thomashoneyman.com/guides/real-world-halogen/using-halogen-components This snippet shows the type signature for a pure Halogen component. It encapsulates state and an event loop but performs no direct effects, allowing for easier reasoning and testing as a state machine. ```PureScript myComponent :: forall m. Component HTML Query Input Output m ``` -------------------------------- ### Defining a Permissive User Type in Haskell Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet illustrates a basic, permissive record type for a 'User' in Haskell. It highlights how a simple 'String' for 'username' can lead to invalid states, as it lacks inherent validation for length or character constraints, allowing for potentially problematic values. ```Haskell type User = { username :: String } ``` -------------------------------- ### Define PureScript Authentication Management Capability Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges The `Authentication` type class in PureScript abstracts the management of user credentials. It provides functions for reading, writing, and deleting `AuthUser` credentials, returning `Either Error` for robust error handling. This capability decouples authentication logic from specific storage mechanisms like local storage, allowing for flexible implementation changes. ```APIDOC -- This class represents authentication and the ability to read / -- write credentials class Monad m <= Authentication m where readCredentials :: m (Either Error AuthUser) writeCredentials :: AuthUser -> m (Either Error Unit) deleteCredentials :: m (Either Error Unit) ``` -------------------------------- ### Define PureScript UserProfile Type Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This PureScript type alias defines the structure for a user profile, including username, optional bio, and optional profile photo. It leverages previously designed types like `Username` and `ProfilePhoto` to ensure type safety and clarity. ```PureScript type UserProfile = { username :: Username , bio :: Maybe String , image :: Maybe ProfilePhoto } ``` -------------------------------- ### PureScript: Simplify AuthUser Type for Authentication Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet refines the `AuthUser` type to contain only essential fields: `Username` for identity and `Token` for authentication. It also provides a `username` accessor function to retrieve the username from an `AuthUser` instance, ensuring only relevant information is exposed for auth-related purposes. ```PureScript data AuthUser = AuthUser Username Token -- AuthUser follows the smart constructor pattern, but we'll want to -- be able to retrieve the username (not the token!) username :: AuthUser -> Username username (AuthUser u _) = u ``` -------------------------------- ### PureScript: Refine Follow Function with UnfollowedAuthor Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet introduces `UnfollowedAuthor` as a newtype wrapper around `UserProfile` to restrict the `follow` function's input. The `follow` function now explicitly requires an `UnfollowedAuthor`, ensuring that only users not currently followed can be targeted, thereby improving type safety and preventing self-following. ```PureScript newtype UnfollowedAuthor = UnfollowedAuthor UserProfile follow :: UnfollowedAuthor -> AuthUser -> Effect Unit ``` -------------------------------- ### PureScript Bio Field Definition Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet shows how to represent an optional user biography field in PureScript. Since the bio can be a `String` or `null` from the server, it is modeled as `Maybe String`, indicating that the field might be present or absent. ```PureScript { ... , bio :: Maybe String } ``` -------------------------------- ### Define PureScript AuthUser with Extensible Profile Fields Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This PureScript data type defines `AuthUser` by combining a `Token` with an extended `UserProfileFields` record. It leverages row polymorphism to include an `email` field, demonstrating how common profile data can be reused and extended for different user contexts. ```PureScript data AuthUser = AuthUser Token (UserProfileFields (email :: Email)) ``` -------------------------------- ### PureScript/Haskell: Model ContactInfo with Valid Sum Types Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet demonstrates how to define a 'ContactInfo' type that guarantees a user always has at least one contact method (email, postal, or both). It contrasts overly restrictive or permissive type definitions with a custom sum type that precisely models the valid states, preventing the creation of invalid 'ContactInfo' objects. ```PureScript -- We can't construct a user with both an email and postal address, -- which we ought to be able to. Not permissive enough! type ContactInfo = Either Email Postal -- We could construct a user that doesn't have an email OR a postal -- address. Too permissive! type ContactInfo = Tuple (Maybe Email) (Maybe Postal) -- Now, if we have a ContactInfo, we know it *must* be valid data ContactInfo = EmailOnly Email | PostalOnly Postal | Both Email Postal ``` -------------------------------- ### Conduit API Profile Entity JSON Structure Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This API documentation snippet presents the JSON structure for a 'profile' entity returned by the Conduit API. It details the fields associated with a user's public profile, including 'username', 'bio', 'image' URL, and a 'following' boolean, serving as a blueprint for data type design. ```APIDOC { "profile": { "username": "jake", "bio": "I work at statefarm", "image": "https://static.productionready.io/images/smiley-cyrus.jpg", "following": false } } ``` -------------------------------- ### Define PureScript Extensible UserProfileFields Type Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This PureScript type alias defines an 'open record' for user profile fields, allowing it to be extended with additional fields using row polymorphism. This design enables the reuse of common profile fields across different record types, such as `UserProfile` and `AuthUser`. ```PureScript -- This type is now an "open record", which means it can be extended -- with more fields. type UserProfileFields r = { username :: Username , bio :: Maybe String , image :: Maybe ProfilePhoto | r } ``` -------------------------------- ### Define RemoteData Type for Purescript State Management Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet introduces the `RemoteData` algebraic data type, a robust solution for modeling the lifecycle of remote data. It precisely defines distinct states: `NotAsked` (initial), `Loading` (in progress), `Failure` (with an error), and `Success` (with data), preventing inconsistent state representations. ```Purescript -- https://pursuit.purescript.org/packages/purescript-remotedata data RemoteData err res = NotAsked | Loading | Failure err | Success res ``` -------------------------------- ### PureScript: Revise Author Type for Follow States Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This `Author` type revision uses sum types to represent the different states of a user profile relative to the current user. It distinguishes between `Following` (a user already followed), `NotFollowing` (a user not yet followed), and `You` (the current user's own profile), enabling more precise type-driven logic for UI and business rules. ```PureScript data Author = Following FollowedAuthor | NotFollowing UnfollowedAuthor | You UserProfile ``` -------------------------------- ### Define Purescript LogType Data Type Source: https://thomashoneyman.com/guides/real-world-halogen/push-effects-to-the-edges This snippet defines the `LogType` algebraic data type, which enumerates the different levels of logging: `Debug`, `Info`, and `Error`. It includes derived instances for `Eq` and `Ord` to allow comparison and ordering of log types. This data type serves as the primary input for categorizing log messages. ```Purescript data LogType = Debug | Info | Error derive instance eqLogType :: Eq LogType derive instance ordLogType :: Ord LogType ``` -------------------------------- ### PureScript Author Data Type for Follow Status Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This PureScript `data` type `Author` is designed to represent the follow status of a user in relation to the authenticated user. It captures three distinct states: `Following` (the authenticated user follows them), `NotFollowing` (the authenticated user does not follow them), and `You` (the profile belongs to the authenticated user). This prevents illegal states like 'following yourself'. ```PureScript data Author = Following UserProfile | NotFollowing UserProfile | You UserProfile ``` -------------------------------- ### PureScript: Using Newtypes for Distinct Identifiers Source: https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions This snippet illustrates the use of `newtype` in PureScript to create distinct types for identifiers like `CustomerId` and `OrderId`, even when they wrap the same primitive type (`Natural`). This prevents accidental arithmetic operations, mixing up argument order, or using the wrong identifier type in functions. ```PureScript newtype CustomerId = CustomerId Natural newtype OrderId = OrderId Natural ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.