Giter Club home page Giter Club logo

shopping-cart-haskell's Introduction

shopping-cart-haskell

CI Status

Haskell version of the Shopping Cart developed in the book Practical FP in Scala.

How to run

Within a Nix shell (run nix-shell - recommended), follow the commands below.

Run web application

cabal new-run shopping-cart

Run tests

cabal new-run shopping-cart-tests

Comparison with the Scala application

The original version of the Shopping Cart has been written in Scala. The Haskell application's design is quite similar.

  • Services are represented using polymorphic records of functions.
  • The Newtypes / Refined duo is used for strongly-typed data.
  • Retry is used for retrying computations compositionally.
  • Servant is used as the default HTTP server.
  • Wreq is used as the default HTTP client.
  • Hedis is used as the default Redis client.
  • PostgreSQL Simple, PostgreSQL Resilient, and Raw Strings QQ are used to handle PostgreSQL stuff.

A polymorphic record of functions looks as follows:

data Brands m = Brands
  { findAll :: m [Brand]
  , create :: BrandName -> m ()
  }

Whereas in Scala, we represent it using traits (although case class / classes would work too):

trait Brands[F[_]] {
  def findAll: F[List[Brand]]
  def create(name: BrandName): F[Unit]
}

We pass them along as explicit dependencies, though, so we don't treat them as typeclasses. In Scala, we use the same encoding for typeclasses (and then pass them implicitly) but in Haskell the encoding differs:

class Brands m where
  findAll :: m [Brand]
  create :: BrandName -> m ()

Typeclasses were used to encode effects such as Background, Logger and Retry:

class Background m where
  schedule :: m a -> Minutes -> m ()

instance Background IO where
  schedule fa mins = void $ async (threadDelay (microseconds mins) >> fa)
    where microseconds Mins {..} = 60000000 * unrefine unMins

In Scala, this is how it is encoded:

trait Background[F[_]] {
  def schedule[A](fa: F[A], duration: FiniteDuration): F[Unit]
}

object Background {
  def apply[F[_]: Background]: Background[F] = implicitly

  implicit def bgInstance[F[_]](implicit S: Supervisor[F], T: Temporal[F]): Background[F] =
    new Background[F] {
      def schedule[A](fa: F[A], duration: FiniteDuration): F[Unit] =
        S.supervise(T.sleep(duration) *> fa).void
    }
}

Having an implicit implementation is how we define coherent instances, though, they can be easily overridden (but this is also possible in Haskell using the right extensions).

Missing features

There are some features I don't plan to implement due to lack of time and motivation.

  • JWT Authentication: if you want to get it done, here's some nice documentation on how to get started.
  • Configuration: I recommend using Dhall. You can have a look at how it's done here.
  • Tests: the machinery is in place but it's a ton of work to write tests. I'll leave that for another day :)

shopping-cart-haskell's People

Contributors

gvolpe 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shopping-cart-haskell's Issues

pin nixpkgs to ensure nix-shell works

Hey, I was checking out your blog post about setting up ghcide. Thought I would
clone your repo and give it a whirl. However, I am running into the following error:

λ ~/code/shopping-cart-haskell/ master nix-shell
error: Package ‘refined-0.4.4’ in /nix/store/3m9z2vfr0fnfdgrshyy70cidhyp5wbkz-nixos-20.09pre217537.d96bd3394b7/nixos/pkgs/development/haskell-modules/hackage-packages.nix:202905 is marked as broken, refusing to evaluate.

a) For `nixos-rebuild` you can set
  { nixpkgs.config.allowBroken = true; }
in configuration.nix to override this.

b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
  { allowBroken = true; }
to ~/.config/nixpkgs/config.nix.

(use '--show-trace' to show detailed location information)

I think the best solution is to pin nixpkgs to a working revision of the repository.

Here are some links that go into how one might do that:

I haven't been following the latest trends in how to setup Haskell projects with
nix so those might already be out of date. I've noticed some people use
https://input-output-hk.github.io/haskell.nix/, but that might be overkill and
the build times are insane unless you setup cachix and pin to a known revision
that the CI ran for and cached.

I tried this revision of the unstable branch where refined is not marked broken:

{
  "url": "https://github.com/nixos/nixpkgs-channels.git",
  "rev": "7e5690c088a542d979afab26a5f0f60f37fe2a98",
  "date": "2020-02-28T20:42:15+01:00",
  "sha256": "1gd9giqngr64ms6gjbbhygm2c53kyrrfxs9yhc8dnsh9blhg8cg4",
  "fetchSubmodules": false,
  "deepClone": false,
  "leaveDotGit": false
}

Generated using:

nix-prefetch-git https://github.com/nixos/nixpkgs-channels.git refs/heads/nixos-unstable > ./nix/nixos-unstable.json --rev 7e5690c088a542d979afab26a5f0f60f37fe2a98

with a release.nix of:

let
  pinnedPkgs = import ./nix/pkgs-from-json.nix { json = ./nix/nixos-unstable.json; };
in
  pinnedPkgs.haskellPackages.callPackage ./default.nix { nixpkgs = pinnedPkgs; }

and ./nix/pkgs-from-json.nix:

{ json }:
let
  bootstrapPkgs = import <nixpkgs> {};
  nixpkgs = builtins.fromJSON (builtins.readFile json);
  src = bootstrapPkgs.fetchFromGitHub {
    owner = "NixOS";
    repo  = "nixpkgs-channels";
    inherit (nixpkgs) rev sha256;
  };
in
  import src {}

Unfortunately, I get this error instead:

...
builder for '/nix/store/jhlxyb0jny3810zls9f9mpwmbzysji5w-cryptonite-0.26.drv' failed with exit code 1
cannot build derivation '/nix/store/k33zyqwj9hpv70kmqkawi209gmxzcvp4-ghc-8.6.5-with-packages.drv': 1 dependencies couldn't be built
error: build of '/nix/store/k33zyqwj9hpv70kmqkawi209gmxzcvp4-ghc-8.6.5-with-packages.drv' failed

I stopped looking at that point. If it's working with your nixpkgs, consider pinning it to that version.

Refined issues

Lovely to see refined used, thank you!

I would consider creating a new datatype for your digits predicate. Size predicates are typically intended for collections of elements. I guess it could be said that an Int is a collection of digits, but when i see Refined (SizeEqualTo 5) Int in a type signature, I definitely don't think of number of digits. That's why i would suggest creating something like

data HasDigits (n :: Nat) = HasDigits

instance (KnownNat n, Integral x) => Predicate (HasDigits n) x where 
  ...

Additionally, refined 0.5 will have Predicate instances for Text, including the one you need.

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.