[Haskell-beginners] Understanding reason for Monad

Travis Cardwell travis.cardwell at extrema.is
Wed Feb 22 07:15:27 UTC 2023


Hi Pietro,

Haskell type classes provide ad-hoc polymorphism.  Essentially, they
allow you to write a function that works with more than one concrete
type.

<https://wiki.haskell.org/Polymorphism#Ad-hoc_polymorphism>

Function `hashPassword` has the following type:

    hashPassword
      :: (MonadRandom m, ByteArray password, ByteArray hash)
      => Int
      -> password
      -> m hash

It has three constraints (listed before the double arrow):

Constraint `MonadRandom m` means that the function works in any monad
that has a `MonadRandom` instance and can therefore be used to generate
random values.  If you click `MonadRandom` in the documentation, you
can see which instances are built in.  There is an `IO` instance, so you
can run `hashPassword` in the `IO` monad.

Constraint `ByteArray password` means that the function accepts a
password of any type that has a `ByteArray` instance.  If you click
`ByteArray` in the documentation, you can see which instances are built
in.  There is a `ByteString` instance, so `hashPassword` can handle
`ByteString` password arguments.

Constraint `ByteArray hash` means that the function can return a hash
of any type that has a `ByteArray` instance.  The concrete type may be
determined by how the return type is used; if your code expects a
`ByteString` hash, then that is what you get.

Here is a simple demo that hashes the first argument, or `password` if
no arguments are provided:

    {-# LANGUAGE OverloadedStrings #-}

    module Main (main) where

    -- https://hackage.haskell.org/package/base
    import System.Environment (getArgs)

    -- https://hackage.haskell.org/package/base16-bytestring
    import qualified Data.ByteString.Base16 as Base16

    -- https://hackage.haskell.org/package/cryptonite
    import qualified Crypto.KDF.BCrypt as BCrypt

    -- https://hackage.haskell.org/package/text
    import qualified Data.Text as T
    import qualified Data.Text.Encoding as TE
    import qualified Data.Text.Encoding.Error as TEE
    import qualified Data.Text.IO as TIO

    main :: IO ()
    main = do
        password <- T.pack . head . (++ ["password"]) <$> getArgs
        TIO.putStrLn $ "password: " <> password
        hashBS <- BCrypt.hashPassword 11 $ TE.encodeUtf8 password
        let hashHex = TE.decodeUtf8With TEE.lenientDecode $ Base16.encode hashBS
        TIO.putStrLn $ "hash: " <> hashHex

This demo runs in the `IO` monad, the password is passed to
`hashPassword` as a `ByteString`, and the hash returned by
`hashPassword` is a `ByteString`.  Note that the password is encoded as
a `ByteString` via `Text` so that it supports UTF-8.

    $ hash-password パスワード
    password: パスワード
    hash: 24326224313124486e474157773750493667744b6b6270354451613275516863667a68702e6756727a414b474542716751755371314949613549314b

Cheers,

Travis


More information about the Beginners mailing list