├── .gitattributes ├── .github └── workflows │ ├── docs.yaml │ └── main.yml ├── .gitignore ├── Dockerfile ├── docs ├── api │ └── index.html ├── examples │ └── index.html ├── index.html ├── index.js ├── install.md ├── readme.md └── style.css ├── examples ├── 01-hello-world.hell ├── 02-interaction.hell ├── 03-press-any-key.hell ├── 04-writing-files.hell ├── 05-lists.hell ├── 06-polymorphism.hell ├── 07-loops.hell ├── 08-tuples.hell ├── 09-processes.hell ├── 10-current-directory.hell ├── 11-env-vars.hell ├── 12-fib.hell ├── 13-concurrency.hell ├── 14-text.hell ├── 15-type-classes.hell ├── 16-if.hell ├── 17-reuse.hell ├── 18-monads.hell ├── 19-blog-generator.hell ├── 20-dollar.hell ├── 21-json.hell ├── 22-records.hell ├── 23-args.hell ├── 24-exitcode.hell ├── 25-sum-types.hell ├── 26-reference-other-types.hell ├── 27-discussion-64.hell ├── 28-trees.hell ├── 29-temp-files.hell ├── 30-process-handlers.hell ├── 31-open-file-handle.hell ├── 32-optparse.hell ├── 33-null-stream.hell ├── 34-field-puns.hell ├── 35-type-sigs.hell ├── 36-these.hell ├── 37-readshow.hell ├── 38-integer.hell ├── 39-day.hell └── 40-utctime.hell ├── flake.lock ├── flake.nix ├── hell.cabal ├── package.yaml ├── scripts ├── check-docs.hell ├── check-examples.hell ├── check.hell ├── gen-docs.hell ├── install-hell.hell ├── readme.md └── static-build.hell ├── src └── Hell.hs ├── stack.yaml └── stack.yaml.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | *.hell linguist-language=Haskell 2 | -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | name: "Generate homepage" 2 | 3 | permissions: 4 | contents: read 5 | pages: write 6 | id-token: write 7 | 8 | on: 9 | push: 10 | branches: [ "main" ] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: actions/configure-pages@v4 21 | - uses: actions/upload-pages-artifact@v3 22 | with: 23 | path: "./docs" 24 | - uses: actions/deploy-pages@v4 25 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | 3 | permissions: 4 | contents: read 5 | pages: write 6 | id-token: write 7 | 8 | on: 9 | push: 10 | branches: [ "main" ] 11 | pull_request: 12 | branches: [ "main" ] 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | jobs: 18 | build: 19 | runs-on: ubuntu-latest 20 | container: 21 | image: "docker://ghcr.io/chrisdone/hell-build:2025-03-04@sha256:ca21e3be038cf1f10fa18306123b4d5f0e2009fe8938cea3afcef7f900bbea71" 22 | 23 | env: 24 | # For the ~/.stack root. 25 | HOME: /root/ 26 | STACK_ROOT: /root/.stack 27 | 28 | # This can be both of these, depending on whether it's a PR or 29 | # main. GitHub Actions is weird. 30 | # 31 | # * "GIT_BRANCH=refs/heads/main" 32 | # * "GIT_BRANCH=cd/2024-08-28-check-examples" 33 | # 34 | GIT_BRANCH: ${{ github.head_ref || github.ref }} 35 | 36 | steps: 37 | - run: | 38 | git clone https://github.com/chrisdone/hell /tmp/hell && \ 39 | cd /tmp/hell && \ 40 | git checkout $GIT_BRANCH 41 | - run: | 42 | cd /tmp/hell && stack build --fast 43 | - run: | 44 | cd /tmp/hell && stack exec hell scripts/check-examples.hell 45 | - run: | 46 | cd /tmp/hell && HOME=/home/chris/ stack exec hell scripts/check-docs.hell 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work 2 | result 3 | dist-newstyle 4 | hell-linux-x86-64bit 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # docker run -d --name hell -it -v`pwd`:`pwd` -w`pwd` ghcr.io/chrisdone/hell-build:2025-03-04 sh 2 | 3 | FROM alpine:20250108 4 | 5 | RUN apk add stack ghc 6 | 7 | ADD stack.yaml /stack.yaml 8 | ADD package.yaml /package.yaml 9 | 10 | RUN stack setup && stack update 11 | 12 | ADD src/Hell.hs /src/Hell.hs 13 | 14 | RUN apk add musl-dev 15 | 16 | RUN stack build 17 | 18 | RUN apk add pandoc 19 | 20 | RUN apk add gmp-static 21 | 22 | RUN apk add git 23 | -------------------------------------------------------------------------------- /docs/api/index.html: -------------------------------------------------------------------------------- 1 |
data () :: *
data Bool :: *
data ByteString :: *
data Char :: *
data Day :: *
data Double :: *
data Either :: * -> * -> *
data ExitCode :: *
data Handle :: *
data IO :: * -> *
data Int :: *
data Integer :: *
data Map :: * -> * -> *
data Maybe :: * -> *
data Set :: * -> *
data Text :: *
data These :: * -> * -> *
data TimeOfDay :: *
data Tree :: * -> *
data UTCTime :: *
data Value :: *
data Vector :: * -> *
$ :: forall a b . (a -> b) -> a -> b
. :: forall a b c . (b -> c) -> (a -> b) -> a -> c
<$> :: forall (f :: * -> *) a b . Functor f => (a -> b) -> f a -> f b
<**> :: forall (f :: * -> *) a b . Applicative f =>
22 | f a -> f (a -> b) -> f b
<*> :: forall (f :: * -> *) a b . Applicative f =>
23 | f (a -> b) -> f a -> f b
<> :: forall m . Semigroup m => m -> m -> m
Alternative.optional :: forall (f :: * -> *) a . Alternative f => f a -> f (Maybe a)
Applicative.pure :: forall (f :: * -> *) a . Applicative f => a -> f a
Argument.help :: forall a . Text -> Mod ArgumentFields a
Argument.metavar :: forall a . Text -> Mod ArgumentFields a
Argument.value :: forall a . a -> Mod ArgumentFields a
Async.concurrently :: forall a b . IO a -> IO b -> IO (a, b)
Async.pooledForConcurrently :: forall a b . [a] -> (a -> IO b) -> IO [b]
Async.pooledForConcurrently_ :: forall a . [a] -> (a -> IO ()) -> IO ()
Async.pooledMapConcurrently :: forall a b . (a -> IO b) -> [a] -> IO [b]
Async.pooledMapConcurrently_ :: forall a . (a -> IO ()) -> [a] -> IO ()
Async.race :: forall a b . IO a -> IO b -> IO (Either a b)
Bool.False :: Bool
Bool.True :: Bool
Bool.bool :: forall a . a -> a -> Bool -> a
Bool.not :: Bool -> Bool
ByteString.getContents :: IO ByteString
ByteString.hGet :: Handle -> Int -> IO ByteString
ByteString.hPutStr :: Handle -> ByteString -> IO ()
ByteString.interact :: (ByteString -> ByteString) -> IO ()
ByteString.readFile :: Text -> IO ByteString
ByteString.readProcess :: ProcessConfig () () () -> IO (ExitCode,ByteString,ByteString)
ByteString.readProcessStdout_ :: ProcessConfig () () () -> IO ByteString
ByteString.readProcess_ :: ProcessConfig () () () -> IO (ByteString,ByteString)
ByteString.writeFile :: Text -> ByteString -> IO ()
Concurrent.threadDelay :: Int -> IO ()
Day.addDays :: Integer -> Day -> Day
Day.diffDays :: Day -> Day -> Integer
Day.fromGregorianValid :: Integer -> Int -> Int -> Maybe Day
Day.iso8601ParseM :: Text -> Maybe Day
Day.iso8601Show :: Day -> Text
Directory.copyFile :: Text -> Text -> IO ()
Directory.createDirectory :: Text -> IO ()
Directory.createDirectoryIfMissing :: Bool -> Text -> IO ()
Directory.doesDirectoryExist :: Text -> IO Bool
Directory.doesFileExist :: Text -> IO Bool
Directory.getCurrentDirectory :: IO Text
Directory.listDirectory :: Text -> IO [Text]
Directory.removeFile :: Text -> IO ()
Directory.renameFile :: Text -> Text -> IO ()
Directory.setCurrentDirectory :: Text -> IO ()
Double.eq :: Double -> Double -> Bool
Double.fromInt :: Int -> Double
Double.mult :: Double -> Double -> Double
Double.plus :: Double -> Double -> Double
Double.readMaybe :: Text -> Maybe Double
Double.show :: Double -> Text
Double.showEFloat :: Maybe Int -> Double -> Text -> Text
Double.showFFloat :: Maybe Int -> Double -> Text -> Text
Double.subtract :: Double -> Double -> Double
Either.Left :: forall a b . a -> Either a b
Either.Right :: forall a b . b -> Either a b
Either.either :: forall a b x . (a -> x) -> (b -> x) -> Either a b -> x
Environment.getArgs :: IO [Text]
Environment.getEnv :: Text -> IO Text
Environment.getEnvironment :: IO [(Text,Text)]
Eq.eq :: forall a . Eq a => a -> a -> Bool
Error.error :: forall a . Text -> a
Exit.ExitFailure :: Int -> ExitCode
Exit.ExitSuccess :: ExitCode
Exit.die :: forall a . Text -> IO a
Exit.exitCode :: forall a . a -> (Int -> a) -> ExitCode -> a
Exit.exitWith :: forall a . ExitCode -> IO a
Flag.help :: forall a . Text -> Mod FlagFields a
Flag.long :: forall a . Text -> Mod FlagFields a
Function.fix :: forall a . (a -> a) -> a
Function.id :: forall a . a -> a
Functor.fmap :: forall (f :: * -> *) a b . Functor f => (a -> b) -> f a -> f b
IO.AppendMode :: IOMode
IO.BlockBuffering :: Maybe Int -> BufferMode
IO.LineBuffering :: BufferMode
IO.NoBuffering :: BufferMode
IO.ReadMode :: IOMode
IO.ReadWriteMode :: IOMode
IO.WriteMode :: IOMode
IO.forM_ :: forall a . [a] -> (a -> IO ()) -> IO ()
IO.hClose :: Handle -> IO ()
IO.hSetBuffering :: Handle -> BufferMode -> IO ()
IO.mapM_ :: forall a . (a -> IO ()) -> [a] -> IO ()
IO.openFile :: Text -> IOMode -> IO Handle
IO.print :: forall a . Show a => a -> IO ()
IO.pure :: forall a . a -> IO a
IO.stderr :: Handle
IO.stdin :: Handle
IO.stdout :: Handle
Int.eq :: Int -> Int -> Bool
Int.fromInteger :: Integer -> Int
Int.mult :: Int -> Int -> Int
Int.plus :: Int -> Int -> Int
Int.readMaybe :: Text -> Maybe Int
Int.show :: Int -> Text
Int.subtract :: Int -> Int -> Int
Int.toInteger :: Int -> Integer
Integer.mult :: Integer -> Integer -> Integer
Integer.plus :: Integer -> Integer -> Integer
Integer.readMaybe :: Text -> Maybe Integer
Integer.subtract :: Integer -> Integer -> Integer
Json.Array :: Vector Value -> Value
Json.Bool :: Bool -> Value
Json.Null :: Value
Json.Number :: Double -> Value
Json.Object :: Map Text Value -> Value
Json.String :: Text -> Value
Json.decode :: ByteString -> Maybe Value
Json.encode :: Value -> ByteString
Json.value :: forall a .
24 | a ->
25 | (Bool -> a) ->
26 | (Text -> a) ->
27 | (Double -> a) ->
28 | (Vector Value -> a) -> (Map Text Value -> a) -> Value -> a
List.all :: forall a . (a -> Bool) -> [a] -> Bool
List.and :: [Bool] -> Bool
List.any :: forall a . (a -> Bool) -> [a] -> Bool
List.break :: forall a . (a -> Bool) -> [a] -> ([a], [a])
List.concat :: forall a . [[a]] -> [a]
List.concatMap :: forall a b . (a -> [b]) -> [a] -> [b]
List.cons :: forall a . a -> [a] -> [a]
List.cycle :: forall a . [a] -> [a]
List.deleteBy :: forall a . (a -> a -> Bool) -> a -> [a] -> [a]
List.drop :: forall a . Int -> [a] -> [a]
List.dropWhile :: forall a . (a -> Bool) -> [a] -> [a]
List.dropWhileEnd :: forall a . (a -> Bool) -> [a] -> [a]
List.elem :: forall a . Eq a => a -> [a] -> Bool
List.elemIndex :: forall a . Eq a => a -> [a] -> Maybe Int
List.elemIndices :: forall a . Eq a => a -> [a] -> [Int]
List.filter :: forall a . (a -> Bool) -> [a] -> [a]
List.find :: forall a . (a -> Bool) -> [a] -> Maybe a
List.findIndex :: forall a . (a -> Bool) -> [a] -> Maybe Int
List.findIndices :: forall a . (a -> Bool) -> [a] -> [Int]
List.foldl' :: forall a b . (b -> a -> b) -> b -> [a] -> b
List.foldr :: forall a b . (a -> b -> b) -> b -> [a] -> b
List.group :: forall a . Eq a => [a] -> [[a]]
List.groupBy :: forall a . (a -> a -> Bool) -> [a] -> [[a]]
List.inits :: forall a . [a] -> [[a]]
List.intercalate :: forall a . [a] -> [[a]] -> [a]
List.intersperse :: forall a . a -> [a] -> [a]
List.isInfixOf :: forall a . Eq a => [a] -> [a] -> Bool
List.isPrefixOf :: forall a . Eq a => [a] -> [a] -> Bool
List.isSubsequenceOf :: forall a . Eq a => [a] -> [a] -> Bool
List.isSuffixOf :: forall a . Eq a => [a] -> [a] -> Bool
List.iterate' :: forall a . (a -> a) -> a -> [a]
List.length :: forall a . [a] -> Int
List.lookup :: forall a b . Eq a => a -> [(a, b)] -> Maybe b
List.map :: forall a b . (a -> b) -> [a] -> [b]
List.mapAccumL :: forall s a b . (s -> a -> (s, b)) -> s -> [a] -> (s, [b])
List.mapAccumR :: forall s a b . (s -> a -> (s, b)) -> s -> [a] -> (s, [b])
List.nil :: forall a . [a]
List.notElem :: forall a . Eq a => a -> [a] -> Bool
List.nubOrd :: forall a . Ord a => [a] -> [a]
List.null :: forall a . [a] -> Bool
List.or :: [Bool] -> Bool
List.partition :: forall a . (a -> Bool) -> [a] -> ([a], [a])
List.permutations :: forall a . [a] -> [[a]]
List.repeat :: forall a . a -> [a]
List.reverse :: forall a . [a] -> [a]
List.scanl' :: forall a b . (b -> a -> b) -> b -> [a] -> [b]
List.scanr :: forall a b . (a -> b -> b) -> b -> [a] -> [b]
List.sort :: forall a . Ord a => [a] -> [a]
List.sortOn :: forall a b . Ord b => (a -> b) -> [a] -> [a]
List.span :: forall a . (a -> Bool) -> [a] -> ([a], [a])
List.splitAt :: forall a . Int -> [a] -> ([a], [a])
List.subsequences :: forall a . [a] -> [[a]]
List.tails :: forall a . [a] -> [[a]]
List.take :: forall a . Int -> [a] -> [a]
List.takeWhile :: forall a . (a -> Bool) -> [a] -> [a]
List.transpose :: forall a . [[a]] -> [[a]]
List.uncons :: forall a . [a] -> Maybe (a, [a])
List.unfoldr :: forall a b . (b -> Maybe (a, b)) -> b -> [a]
List.zip :: forall a b . [a] -> [b] -> [(a, b)]
List.zipWith :: forall a b c . (a -> b -> c) -> [a] -> [b] -> [c]
Map.adjust :: forall k a . Ord k => (a -> a) -> k -> Map k a -> Map k a
Map.all :: forall k a . (a -> Bool) -> Map k a -> Bool
Map.any :: forall k a . (a -> Bool) -> Map k a -> Bool
Map.delete :: forall k a . Ord k => k -> Map k a -> Map k a
Map.elems :: forall k a . Map k a -> [a]
Map.filter :: forall k a . (a -> Bool) -> Map k a -> Map k a
Map.filterWithKey :: forall k a . (k -> a -> Bool) -> Map k a -> Map k a
Map.fromList :: forall k a . Ord k => [(k, a)] -> Map k a
Map.insert :: forall k a . Ord k => k -> a -> Map k a -> Map k a
Map.insertWith :: forall k a . Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.keys :: forall k a . Map k a -> [k]
Map.lookup :: forall k a . Ord k => k -> Map k a -> Maybe a
Map.map :: forall a b k . (a -> b) -> Map k a -> Map k b
Map.singleton :: forall k a . Ord k => k -> a -> Map k a
Map.size :: forall k a . Map k a -> Int
Map.toList :: forall k a . Map k a -> [(k, a)]
Map.unionWith :: forall k a . Ord k =>
29 | (a -> a -> a) -> Map k a -> Map k a -> Map k a
Maybe.Just :: forall a . a -> Maybe a
Maybe.Nothing :: forall a . Maybe a
Maybe.listToMaybe :: forall a . [a] -> Maybe a
Maybe.mapMaybe :: forall a b . (a -> Maybe b) -> [a] -> [b]
Maybe.maybe :: forall a b . b -> (a -> b) -> Maybe a -> b
Monad.bind :: forall (m :: * -> *) a b . Monad m => m a -> (a -> m b) -> m b
Monad.forM :: forall a b (m :: * -> *) . Monad m => [a] -> (a -> m b) -> m [b]
Monad.forM_ :: forall a (m :: * -> *) . Monad m => [a] -> (a -> m ()) -> m ()
Monad.mapM :: forall a b (m :: * -> *) . Monad m => (a -> m b) -> [a] -> m [b]
Monad.mapM_ :: forall a (m :: * -> *) . Monad m => (a -> m ()) -> [a] -> m ()
Monad.return :: forall a (m :: * -> *) . Monad m => a -> m a
Monad.sequence :: forall a (m :: * -> *) . Monad m => [m a] -> m [a]
Monad.then :: forall (m :: * -> *) a b . Monad m => m a -> m b -> m b
Monad.when :: forall (m :: * -> *) . Monad m => Bool -> m () -> m ()
Option.help :: forall a . Text -> Mod OptionFields a
Option.long :: forall a . Text -> Mod OptionFields a
Option.value :: forall a . a -> Mod OptionFields a
Options.execParser :: forall a . ParserInfo a -> IO a
Options.flag :: forall a . a -> a -> Mod FlagFields a -> Parser a
Options.flag' :: forall a . a -> Mod FlagFields a -> Parser a
Options.fullDesc :: forall a . InfoMod a
Options.helper :: forall a . Parser (a -> a)
Options.info :: forall a . Parser a -> InfoMod a -> ParserInfo a
Options.strArgument :: Mod ArgumentFields Text -> Parser Text
Options.strOption :: Mod OptionFields Text -> Parser Text
Options.switch :: Mod FlagFields Bool -> Parser Bool
Ord.gt :: forall a . Ord a => a -> a -> Bool
Ord.lt :: forall a . Ord a => a -> a -> Bool
Process.nullStream :: forall (a :: StreamType) . StreamSpec a ()
Process.proc :: Text -> [Text] -> ProcessConfig () () ()
Process.runProcess :: forall a b c . ProcessConfig a b c -> IO ExitCode
Process.runProcess_ :: forall a b c . ProcessConfig a b c -> IO ()
Process.setEnv :: [(Text,Text)] -> ProcessConfig () () () -> ProcessConfig () () ()
Process.setStderr :: forall stdin stdout stderr stderr' .
30 | StreamSpec 'STOutput stderr' ->
31 | ProcessConfig stdin stdout stderr ->
32 | ProcessConfig stdin stdout stderr'
Process.setStdin :: forall stdin stdin' stdout stderr .
33 | StreamSpec 'STInput stdin' ->
34 | ProcessConfig stdin stdout stderr ->
35 | ProcessConfig stdin' stdout stderr
Process.setStdout :: forall stdin stdout stdout' stderr .
36 | StreamSpec 'STOutput stdout' ->
37 | ProcessConfig stdin stdout stderr ->
38 | ProcessConfig stdin stdout' stderr
Process.setWorkingDir :: forall a b c . Text -> ProcessConfig a b c -> ProcessConfig a b c
Process.useHandleClose :: forall (a :: StreamType) . Handle -> StreamSpec a ()
Process.useHandleOpen :: forall (a :: StreamType) . Handle -> StreamSpec a ()
Record.get :: forall (k :: Symbol) a (t :: Symbol) (xs :: List) .
39 | Tagged t (Record xs) -> a
Record.modify :: forall (k :: Symbol) a (t :: Symbol) (xs :: List) .
40 | (a -> a) -> Tagged t (Record xs) -> Tagged t (Record xs)
Record.set :: forall (k :: Symbol) a (t :: Symbol) (xs :: List) .
41 | a -> Tagged t (Record xs) -> Tagged t (Record xs)
Set.delete :: forall a . Ord a => a -> Set a -> Set a
Set.difference :: forall a . Ord a => Set a -> Set a -> Set a
Set.fromList :: forall a . Ord a => [a] -> Set a
Set.insert :: forall a . Ord a => a -> Set a -> Set a
Set.intersection :: forall a . Ord a => Set a -> Set a -> Set a
Set.member :: forall a . Ord a => a -> Set a -> Bool
Set.singleton :: forall a . Ord a => a -> Set a
Set.size :: forall a . Set a -> Int
Set.toList :: forall a . Set a -> [a]
Set.union :: forall a . Ord a => Set a -> Set a -> Set a
Show.show :: forall a . Show a => a -> Text
Temp.withSystemTempDirectory :: forall a . Text -> (Text -> IO a) -> IO a
Temp.withSystemTempFile :: forall a . Text -> (Text -> Handle -> IO a) -> IO a
Text.appendFile :: Text -> Text -> IO ()
Text.breakOn :: Text -> Text -> (Text,Text)
Text.concat :: [Text] -> Text
Text.decodeUtf8 :: ByteString -> Text
Text.drop :: Int -> Text -> Text
Text.dropEnd :: Int -> Text -> Text
Text.encodeUtf8 :: Text -> ByteString
Text.eq :: Text -> Text -> Bool
Text.getContents :: IO Text
Text.getLine :: IO Text
Text.hPutStr :: Handle -> Text -> IO ()
Text.interact :: (Text -> Text) -> IO ()
Text.intercalate :: Text -> [Text] -> Text
Text.isInfixOf :: Text -> Text -> Bool
Text.isPrefixOf :: Text -> Text -> Bool
Text.isSuffixOf :: Text -> Text -> Bool
Text.length :: Text -> Int
Text.lines :: Text -> [Text]
Text.putStr :: Text -> IO ()
Text.putStrLn :: Text -> IO ()
Text.readFile :: Text -> IO Text
Text.readProcess :: ProcessConfig () () () -> IO (ExitCode,Text,Text)
Text.readProcessStdout_ :: ProcessConfig () () () -> IO Text
Text.readProcess_ :: ProcessConfig () () () -> IO (Text,Text)
Text.replace :: Text -> Text -> Text -> Text
Text.reverse :: Text -> Text
Text.setStdin :: Text -> ProcessConfig () () () -> ProcessConfig () () ()
Text.splitOn :: Text -> Text -> [Text]
Text.strip :: Text -> Text
Text.stripPrefix :: Text -> Text -> Maybe Text
Text.stripSuffix :: Text -> Text -> Maybe Text
Text.take :: Int -> Text -> Text
Text.takeEnd :: Int -> Text -> Text
Text.toLower :: Text -> Text
Text.toUpper :: Text -> Text
Text.unlines :: [Text] -> Text
Text.unwords :: [Text] -> Text
Text.words :: Text -> [Text]
Text.writeFile :: Text -> Text -> IO ()
These.That :: forall a b . b -> These a b
These.These :: forall a b . a -> b -> These a b
These.This :: forall a b . a -> These a b
These.these :: forall a b c .
42 | (a -> c) -> (b -> c) -> (a -> b -> c) -> These a b -> c
TimeOfDay.makeTimeOfDayValid :: Int -> Int -> Double -> Maybe TimeOfDay
TimeOfDay.midday :: TimeOfDay
TimeOfDay.midnight :: TimeOfDay
TimeOfDay.timeOfDayToTime :: TimeOfDay -> Double
TimeOfDay.timeToTimeOfDay :: Double -> TimeOfDay
TimeOfDay.todHour :: TimeOfDay -> Int
TimeOfDay.todMin :: TimeOfDay -> Int
TimeOfDay.todSec :: TimeOfDay -> Double
Timeout.timeout :: forall a . Int -> IO a -> IO (Maybe a)
Tree.Node :: forall a . a -> [Tree a] -> Tree a
Tree.flatten :: forall a . Tree a -> [a]
Tree.foldTree :: forall a b . (a -> [b] -> b) -> Tree a -> b
Tree.levels :: forall a . Tree a -> [[a]]
Tree.map :: forall a b . (a -> b) -> Tree a -> Tree b
Tree.unfoldTree :: forall a b . (b -> (a, [b])) -> b -> Tree a
Tuple.(,) :: forall a b . a -> b -> (a, b)
Tuple.(,,) :: forall a b c . a -> b -> c -> (a, b, c)
Tuple.(,,,) :: forall a b c d . a -> b -> c -> d -> (a, b, c, d)
UTCTime.UTCTime :: Day -> Double -> UTCTime
UTCTime.addUTCTime :: Double -> UTCTime -> UTCTime
UTCTime.diffUTCTime :: UTCTime -> UTCTime -> Double
UTCTime.getCurrentTime :: IO UTCTime
UTCTime.iso8601ParseM :: Text -> Maybe UTCTime
UTCTime.iso8601Show :: UTCTime -> Text
UTCTime.utctDay :: UTCTime -> Day
UTCTime.utctDayTime :: UTCTime -> Double
Vector.fromList :: forall a . [a] -> Vector a
Vector.toList :: forall a . Vector a -> [a]
#!/usr/bin/env hell
23 | = Text.putStrLn "Hello, World!" main
= do
26 | main "Please enter your name and hit ENTER:"
27 | Text.putStrLn <- Text.getLine
28 | name "Thanks, your name is: "
29 | Text.putStrLn Text.putStrLn name
= do
32 | main IO.hSetBuffering IO.stdin IO.NoBuffering
33 | IO.hSetBuffering IO.stdout IO.NoBuffering
34 |
35 | "Please press any key ... "
36 | Text.putStr <- ByteString.hGet IO.stdin 1
37 | chunk
38 | IO.hSetBuffering IO.stdout IO.LineBuffering
39 | "OK!" Text.putStrLn
= do
42 | main let fp = "foo.txt"
43 | "Hello, "
44 | Text.writeFile fp "World!"
45 | Text.appendFile fp <- Text.readFile fp
46 | text Text.putStrLn text
= do
49 | main let is = List.iterate' (Int.plus 1) 0
50 | let xs = ["Hello, ", "World!"]
51 | "OK!"
52 | Text.putStrLn Monad.forM_ (List.zip is xs) \(i,x) -> do
53 | IO.print i
54 |
55 | Text.putStrLn xIO.print $ List.foldl' Int.plus 0 $ List.take 10 is
= do
58 | main let x = "Hello!"
59 |
60 | Text.putStrLn (Function.id x)let lengths = List.map Text.length ["foo", "mu"]
61 | IO.mapM_ (\i -> Text.putStrLn (Int.show i)) lengths
= do
64 | main IO.mapM_ Text.putStrLn ["Hello, ", "World!"]
65 |
66 | loop :: IO ()) -> do
67 | Function.fix (\("Ahhhhh! More?"
68 | Text.putStrLn <- Text.getLine
69 | l loop)
= do
72 | main let demo = \(x, y) -> y
73 | let foobar = (123, "foo")
74 |
75 | Text.putStrLn (demo foobar)
76 | let (foo,bar) = (123, "foo")
77 |
78 | Text.putStrLn bar
79 | let typeSigsWork :: () = ()
80 |
81 | Monad.return ()
= do
84 | main "OK"
85 | Text.putStrLn <- ByteString.readProcess (Process.proc "ls" ["-al"])
86 | (code, out, err) ByteString.hPutStr IO.stdout out
87 | ByteString.hPutStr IO.stdout err
88 |
89 | <- Text.readProcess_ (Process.proc "df" ["-h", "/"])
90 | (out, err) IO.stdout out
91 | Text.hPutStr IO.stdout err
92 | Text.hPutStr
93 | <- Process.runProcess (Process.proc "false" [])
94 | code
95 | "echo" ["Hello, World!"])
96 | Process.runProcess_ (Process.proc
97 | let config = Process.proc "false" []
98 | <- Process.runProcess config
99 | code
100 | $ Process.setWorkingDir "/etc/" $ Process.proc "pwd" []
101 | Process.runProcess
102 | "Done." Text.putStrLn
= do
105 | main <- Directory.getCurrentDirectory
106 | dir
107 | Text.putStrLn dir Directory.setCurrentDirectory dir
= do
110 | main <- Environment.getEnvironment
111 | env <-
112 | (out, err)
113 | Text.readProcess_ ("HELL_DEMO", "wibble") env)
114 | Process.setEnv (List.cons ("env" [])
115 | (Process.proc
116 | )IO.stdout out Text.hPutStr
= do
119 | main Int.show (Main.fib 30))
120 | Text.putStrLn (
121 | =
122 | fib
123 | Function.fix->
124 | (\fib i Bool.bool
125 | Bool.bool
126 | (Int.plus (fib (Int.subtract 1 i))
127 | (Int.subtract 2 i)))
128 | (fib (1
129 | Int.eq i 1))
130 | (0
131 | Int.eq i 0)
132 | ( )
= do
135 | main
136 | -- Run two things concurrently and return both results
137 | <-
138 | (left, right)
139 | Async.concurrently"https://worldtimeapi.org/api/timezone/Europe/London")
140 | (Main.curl "https://worldtimeapi.org/api/timezone/Europe/Rome")
141 | (Main.curl
142 | Text.putStrLn left
143 | Text.putStrLn right
144 | -- Run two things concurrently and return the one that completes first
145 | <-
146 | result
147 | Async.race"https://worldtimeapi.org/api/timezone/Europe/London")
148 | (Main.curl "https://worldtimeapi.org/api/timezone/Europe/Rome")
149 | (Main.curl Either.either Text.putStrLn Text.putStrLn result
150 |
151 | = \url -> do
152 | curl <- Text.readProcess_ (Process.proc "curl" [url])
153 | (out, err) IO.pure out
= do
156 | main "Hello, ", "World!"])
157 | Text.putStrLn (Text.concat [3 "Hello, World!")
158 | Text.putStrLn (Text.take 3 "Hello, World!")
159 | Text.putStrLn (Text.drop " Hello, World! ")
160 | Text.putStrLn (Text.strip ", " ["Hello","World!"]) Text.putStrLn (Text.intercalate
= do
163 | main Show.show 123)
164 | Text.putStrLn (Show.show Bool.True)
165 | Text.putStrLn (
166 | <- Environment.getEnvironment
167 | env Maybe.maybe
168 | "Seems the environment variable is not there.")
169 | (Text.putStrLn -> Text.putStrLn (Text.concat ["HOME is ", path]))
170 | (\path "HOME" env) (List.lookup
= do
173 | main if List.and [Eq.eq (Int.plus 1 1) 2,
174 | Ord.lt (Int.plus 1 1) 3,
175 | Eq.eq (Text.concat ["Hello, World!"]) "Hello, World!"]
176 | then Text.putStrLn "OK, List.and works."
177 | else Text.putStrLn "Uh, oh?"
178 |
179 | if List.or [Eq.eq 1 2,
180 | Eq.eq "x" "x"]
181 | then Text.putStrLn "OK, List.or works."
182 | else Text.putStrLn "Uh, oh?"
183 |
184 | if Bool.not (Eq.eq 1 2)
185 | then Text.putStrLn "OK, Bool.not works."
186 | else Text.putStrLn "Uh, oh?"
-- Technically you're not supposed to be able to do code re-use in
189 | -- Hell, but presently the desugarer inlines everything into `main`
190 | -- prior to type-checking, and ignores declarations that aren't
191 | -- reachable by main.
192 |
193 | = do
194 | main 1
195 | Main.foo "blah"
196 | Main.foo = \x -> Text.putStrLn (Show.show x)
197 | foo = Int.plus 4 "hi" bar
= do
200 | main <- Environment.getEnvironment
201 | env
202 | -- Maybe monad works!
203 | Maybe.maybe (Text.putStrLn "Oops!") Text.putStrLn
204 | do path <- List.lookup "PATH" env
205 | (<- Functor.fmap Text.reverse $ List.lookup "HOME" env
206 | home Monad.return (Text.concat [path, " and ", home]))
207 |
208 | -- Either monad works!
209 | Either.either Text.putStrLn Text.putStrLn
210 | do x <- Main.parse "foo"
211 | (<- Main.parse "foo"
212 | y Monad.return (Text.concat [x,y]))
213 |
214 | = \s ->
215 | parse if Eq.eq s "foo"
216 | then Either.Right "foooo :-)"
217 | else Either.Left "oh noes!"
-- This is a copy of the script that generates my blog.
220 |
221 | -- Dependencies:
222 | --
223 | -- hell-2024-02-07
224 | -- pandoc-3.1.11.1
225 |
226 | -- Main entry point just generates the complete blog every time.
227 | --
228 | --
229 | = Main.generate
230 | main
231 | -- The posts are listed under ./posts in this format:
232 | --
233 | -- dijkstra-haskell-java.markdown
234 | -- reasoning-violently.md
235 | -- god-mode.markdown
236 | -- emacs-mail.markdown
237 | --
238 | -- .md or .markdown files, the extension doesn't matter.
239 | --
240 | = do
241 | generate <- Main.generatePosts
242 | posts
243 | Main.generateArchive posts
244 | Main.generateRSS posts
245 | -- Write out posts/$post/index.html per $post.
246 | --
247 | = do
248 | generatePosts <- Directory.listDirectory "posts"
249 | posts $ Text.concat ["Generating ", Show.show (List.length posts), " posts ..."]
250 | Text.putStrLn -> do
251 | Async.pooledForConcurrently posts \post <- Text.readFile $ Text.concat ["posts/", post]
252 | contents Maybe.maybe
253 | "Couldn't parse the article!")
254 | (Error.error -> do
255 | (\(date, title) <- Main.render post
256 | rendered Monad.return (post, date, title, rendered))
257 | $ Main.parse contents
258 |
259 | -- Generate the /posts/ page.
260 | --
261 | = \posts -> do
262 | generateArchive "Generating archive ..."
263 | Text.putStrLn let rows =
264 |
265 | Text.concat$ List.map
266 | ->
267 | (\(post, date, title, content)
268 | Text.concat ["<tr><td><a href='",
269 |
270 | Main.filename post,"'>",
271 |
272 | Main.strip title,"</td><td>",
273 |
274 | date,"</td></tr>"
275 |
276 | ])$ List.reverse
277 | $ List.sortOn (\(post, date, title, content) -> date)
278 | $ posts
279 | let table = Text.concat [
280 | "---\n",
281 | "title: Archive\n",
282 | "---\n",
283 | "<table id='archive' style='line-height:2em'>",
284 |
285 | rows,"</table>"
286 |
287 | ]<-
288 | (out, err)
289 | Text.readProcess_$ Text.setStdin table
290 | $ Process.proc "pandoc" ["--standalone","--template","templates/posts.html"]
291 | "webroot/posts/index.html" out
292 | Text.writeFile
293 | -- Contents of an article looks like this:
294 | --
295 | -- ---
296 | -- date: 2011-04-10
297 | -- title: ‘amb’ operator and the list monad
298 | -- description: ‘amb’ operator and the list monad
299 | -- author: Chris Done
300 | -- tags: haskell, designs
301 | -- ---
302 | --
303 | -- We're only interested in the date and the title. The rest is
304 | -- redundant.
305 | --
306 | = \article -> do
307 | parse <- Text.stripPrefix "---" article
308 | sansPrefix let (preamble, _content) = Text.breakOn "---" sansPrefix
309 | let lines = Text.splitOn "\n" preamble
310 | let pairs = List.map (\line -> do let (key, value) = Text.breakOn ":" line
311 | 1 value)))
312 | (key, Text.strip (Text.drop lines
313 | <- List.lookup "date" pairs
314 | date <- List.lookup "title" pairs
315 | title Monad.return (date, title)
316 |
317 | -- A post consists of a date, title and markdown.
318 | --
319 | -- Rendering them is easy, just run pandoc and apply an HTML template.
320 | = \post -> do
321 | render let targetDir =
322 | "webroot/posts/", Main.filename post]
323 | Text.concat [let targetFile = Text.concat [targetDir, "/index.html"]
324 | <- Text.readProcess_ (Process.proc "pandoc" ["--standalone","--template","templates/post.html",Text.concat ["posts/", post]])
325 | (out, err) Bool.True targetDir
326 | Directory.createDirectoryIfMissing
327 | Text.writeFile targetFile outMonad.return out
328 |
329 | -- Filename stripped of .md/.markdown.
330 | = \post -> Text.replace ".md" "" (Text.replace ".markdown" "" post)
331 | filename
332 | -- Strip out quotes from "foo".
333 | = \title ->
334 | strip Maybe.maybe title Function.id do
335 | <- Text.stripPrefix "\"" title
336 | title' "\"" title'
337 | Text.stripSuffix
338 | -- Generate the /rss.xml page.
339 | --
340 | = \posts0 -> do
341 | generateRSS let posts1 = List.reverse $ List.sortOn (\(post, date, title, content) -> date) posts0
342 | <- Monad.forM posts1 \(post, date, title, content) -> do
343 | posts <- Text.readProcessStdout_ $ Text.setStdin date $ Process.proc "date" ["-R", "-f", "/dev/stdin"]
344 | date' Monad.return (post, date', title, content)
345 | "Generating rss.xml ..."
346 | Text.putStrLn let items =
347 |
348 | Text.unlines$ List.map
349 | ->
350 | (\(post, date, title, content)
351 | Text.concat ["<item>",
352 | "<title><![CDATA[", Main.strip title, "]]></title>",
353 | "<link>https://chrisdone.com/posts/", Main.filename post, "</link>",
354 | "<guid>https://chrisdone.com/posts/", Main.filename post, "</guid>",
355 | "<description><![CDATA[", content, "]]></description>",
356 | "<pubDate>", date, "</pubDate>",
357 | "<dc:creator>Chris Done</dc:creator>",
358 | "</item>"
359 |
360 | ])
361 | postslet xml = Text.unlines [
362 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>",
363 | "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\">",
364 | "<channel>",
365 | "<title>Chris Done's Blog</title>",
366 | "<link>https://chrisdone.com</link>",
367 | "<description><![CDATA[Blog all about programming, especially in Haskell since 2008!]]></description>",
368 | "<atom:link href=\"https://chrisdone.com/rss.xml\" rel=\"self\" type=\"application/rss+xml\" />",
369 | "<lastBuildDate>Wed, 22 Dec 2021 00:00:00 UT</lastBuildDate>",
370 |
371 | items,"</channel>",
372 | "</rss>"
373 |
374 | ]"webroot/rss.xml" xml Text.writeFile
= Text.putStrLn . Text.reverse $ "Foo!" main
= do
379 | main ByteString.writeFile "demo.json" $
380 | $
381 | Json.encode Json.Object $ Map.fromList [
382 | "name", Json.String "Chris"),
383 | ("age", Json.Number 99.123)
384 | (
385 | ]<- ByteString.readFile "demo.json"
386 | bytes ByteString.hPutStr IO.stdout bytes
387 | $
388 | Text.putStrLn Maybe.maybe "Bad parse."
389 |
390 | (Json.value"null"
391 | -> Text.concat ["bool", Show.show str])
392 | (\str -> Text.concat ["str", Show.show str])
393 | (\str -> Text.concat ["dub", Show.show dub])
394 | (\dub -> "Array!")
395 | (\arr -> "Object."))
396 | (\obj $ Json.decode bytes
397 | "demo.json" Directory.removeFile
data Person = Person { age :: Int, name :: Text }
400 |
401 | = do
402 | main $ Record.get @"name" Main.person
403 | Text.putStrLn $ Record.get @"name" $ Record.set @"name" "Mary" Main.person
404 | Text.putStrLn $ Record.get @"name" $ Record.modify @"name" Text.reverse Main.person
405 | Text.putStrLn
406 | =
407 | person Main.Person { name = "Chris", age = 23 }
= do
410 | main <- Environment.getArgs
411 | args Monad.forM_ args IO.print
= do
414 | main <- ByteString.readProcess (Process.proc "ls" ["-al"])
415 | (code, out, err)
416 | -- Accessor style
417 |
418 | Exit.exitCode"All good!")
419 | (Text.putStrLn -> IO.print i)
420 | (\i
421 | code
422 | -- Validation style
423 | if Eq.eq code Exit.ExitSuccess
424 | then Text.putStrLn "OK, good!"
425 | else Text.putStrLn "Oh, sad."
data Value = Text Text | Number Int
428 |
429 | data Rating = Good | Bad | Ugly
430 |
431 | = do
432 | main let printIt = \x ->
433 | case x of
434 | Text.putStrLn Number i -> Show.show i
435 | Text t -> t
436 | $ Main.Number 123
437 | printIt $ Main.Text "abc"
438 | printIt Monad.mapM_ printIt [Main.Number 123,Main.Text "abc"]
439 | $ case Main.Good of
440 | Text.putStrLn Good -> "Good!"
441 | Bad -> "Bad!"
442 | Ugly -> "Ugly!"
-- User-defined types can reference other types now.
445 | data Person = Person {
446 | name :: Text,
447 | address :: Main.Address,
448 | status :: Main.Status
449 |
450 | }data Status = Retired | Working
451 | data Address = Address {
452 | line1 :: Text, line2 :: Text
453 |
454 | }= do
455 | main let p :: Main.Person = Main.Person {
456 | = "Chris",
457 | name = Main.Address { line1 = "1 North Pole", line2 = "Earth" },
458 | address = Main.Retired
459 | status
460 | }$ Record.get @"name" p
461 | Text.putStrLn $
462 | Text.putStrLn @"line1" $
463 | Record.get @"address" @Main.Address p
464 | Record.get -- ^ Unfortunately this is needed or else the
465 | -- nested access causes an ambiguous type
466 | -- variable. But it's not too bad.
467 | case Record.get @"status" @Main.Status p of
468 | Retired -> Text.putStrLn "Retired"
469 | Working -> Text.putStrLn "Working"
-- <https://github.com/chrisdone/hell/discussions/64>
472 | --
473 | -- Previously:
474 | --
475 | -- hell: Unification error: Couldn't match type
476 |
477 | -- "Main.MySum"
478 |
479 | -- against type
480 |
481 | -- "MySum"
482 |
483 | data MyRecord = MyRecord {sum :: Main.MySum}
484 |
485 | data MySum = MySumL | MySumR
486 |
487 | = do
488 | main let myRecord = Main.MyRecord {sum = Main.MySumR}
489 | "hello world" Text.putStrLn
-- Basic example of a tree data structure.
492 | = do
493 | main let tree =
494 | Tree.Node "1" [
495 | Tree.Node "1.a" [],
496 | Tree.Node "1.b" [
497 | Tree.Node "1.b.x" []
498 |
499 | ]
500 | ]-- Do a trivial map, include the length of the tag in the nodes.
501 | let tree' = Tree.map (\a -> (a, Text.length a)) tree
502 | -- Write the tree out in a Lisp syntax.
503 |
504 | Tree.foldTree-> do
505 | (\(a, len) children "("
506 | Text.putStr
507 | Text.putStr a" "
508 | Text.putStr $ Show.show len
509 | Text.putStr Monad.forM_ children (\m -> do Text.putStr " "; m)
510 | ")")
511 | Text.putStr tree'
= do
514 | main "example" \dirPath -> do
515 | Temp.withSystemTempDirectory $ Text.concat ["Created temp directory ", dirPath]
516 | Text.putStrLn
517 | "example" \filePath handle -> do
518 | Temp.withSystemTempFile $ Text.concat ["Created temp file ", filePath] Text.putStrLn
= do
521 | main -- 1. close the handle after the process
522 | "example" \filePath handle -> do
523 | Temp.withSystemTempFile $ Text.concat ["Created temp file ", filePath]
524 | Text.putStrLn let proc = Process.setStdout (Process.useHandleClose handle) $
525 | "ls" ["-al"]
526 | Process.proc
527 | Process.runProcess_ proc<- Text.readFile filePath
528 | contents
529 | Text.putStrLn contents
530 | -- 2. keep the handle open after the process
531 | "example-open" \filePath handle -> do
532 | Temp.withSystemTempFile $ Text.concat ["Created temp file ", filePath]
533 | Text.putStrLn let proc0 = Process.setStdout (Process.useHandleOpen handle) $
534 | "echo" ["hello"]
535 | Process.proc -- second time around we we make sure to close the handle
536 | -- so we can then read the file later
537 | let proc1 = Process.setStdout (Process.useHandleClose handle) $
538 | "echo" ["world"]
539 | Process.proc
540 | Process.runProcess_ proc0
541 | Process.runProcess_ proc1<- Text.readFile filePath
542 | contents
543 | Text.putStrLn contents
544 | -- 3. manually close the handle
545 | "example-manual-close" \filePath handle -> do
546 | Temp.withSystemTempFile $ Text.concat ["Created temp file ", filePath]
547 | Text.putStrLn let proc = Process.setStdout (Process.useHandleOpen handle) $
548 | "echo" ["hello"]
549 | Process.proc
550 | Process.runProcess_ proc-- manually close the handle so we can open the file to be read
551 | IO.hClose handle
552 | <- Text.readFile filePath
553 | contents Text.putStrLn contents
= do
556 | main let filepath = "out.txt"
557 | <- IO.openFile filepath IO.WriteMode
558 | handle let proc = Process.setStdout (Process.useHandleClose handle) $
559 | "ls" ["-al"]
560 | Process.proc
561 | Process.runProcess_ procIO.hClose handle
562 |
563 | <- Text.readFile filepath
564 | contents Text.putStrLn contents
-- Includes example of Semigroup.
567 | data Opts = Opts {
568 | quiet :: Bool,
569 | filePath :: Maybe Text
570 |
571 | }=
572 | options -> Main.Opts { quiet = quiet, filePath = path })
573 | (\quiet path <$> Options.switch (Flag.long "quiet" <> Flag.help "Be quiet?")
574 | <*> (Alternative.optional $ Options.strOption (Option.long "path" <> Option.help "The filepath to export"))
575 | = do
576 | main <- Options.execParser (Options.info (Main.options <**> Options.helper) Options.fullDesc)
577 | opts $ Maybe.maybe "No file path" Function.id (Record.get @"filePath" opts)
578 | Text.putStrLn $ Show.show @Bool $ Record.get @"quiet" opts Text.putStrLn
= do
581 | main -- discard stdout
582 | $ Process.setStdout Process.nullStream $ Process.proc "ls" [] Process.runProcess_
data Foo = Foo { bar, mu :: Int }
585 | = do
586 | main let bar = 123
587 | let mu = 666
588 | let r = Main.Foo{bar,mu}
589 | IO.print $ Record.get @"bar" @Int r
data Foo = Foo { bar, mu :: Int }
592 | main :: IO () =
593 |
594 | Main.foo
595 | = do
596 | foo let bar = 123
597 | let mu = 666
598 | let r = Main.Foo{bar,mu}
599 | IO.print $ (Record.get @"bar" r :: Int)
= do
602 | main let things = [These.This 1, These.That "hello", These.These 2 "bonjour"]
603 |
604 | Monad.forM_ things $ \thing -> Text.putStrLn $
605 |
606 | These.these-> Show.show i)
607 | (\i -> s)
608 | (\s -> Text.concat [Show.show i, " ", s])
609 | (\i s thing
-- Reading and showing numbers
612 |
613 | = do
614 | main -- Reading ints and floats
615 |
616 | let mint = Int.readMaybe "123"
617 | let mdouble = Double.readMaybe "123.456"
618 | Maybe.maybe (IO.pure ()) IO.print mint
619 | Maybe.maybe (IO.pure ()) IO.print mdouble
620 |
621 | "Generic"
622 | Text.putStrLn $ Double.show 123456789.123456789
623 | Text.putStrLn $ Double.show 123.0
624 | Text.putStrLn "Scientific"
625 | Text.putStrLn $ Double.showEFloat Maybe.Nothing 123.0 ""
626 | Text.putStrLn $ Double.showEFloat Maybe.Nothing 123456789.123456789 ""
627 | Text.putStrLn $ Double.showEFloat (Maybe.Just 3) 123456789.123456789 ""
628 | Text.putStrLn "Decimal"
629 | Text.putStrLn $ Double.showFFloat Maybe.Nothing 123456789.123456789 ""
630 | Text.putStrLn $ Double.showFFloat (Maybe.Just 3) 123456789.123456789 ""
631 | Text.putStrLn $ Double.showFFloat (Maybe.Just 3) 123456789.0 ""
632 | Text.putStrLn $ Double.showFFloat Maybe.Nothing 123456789.0 "" Text.putStrLn
-- prints: 18446744073709551614
635 | = do
636 | main IO.print $
637 | Integer.plus
638 | Int.toInteger 9223372036854775807)
639 | (Int.toInteger 9223372036854775807) (
= do
642 | main day1 :: Day <-
643 | Maybe.maybe (Error.error "Invalid") IO.pure $ Day.fromGregorianValid (Int.toInteger 2025) 08 09
644 | <- Maybe.maybe (Error.error "Invalid") IO.pure $ Day.iso8601ParseM "2025-08-09"
645 | day2 IO.print $ Eq.eq day1 day2 -- True
646 | $ Day.iso8601Show day1 -- 2025-08-09 Text.putStrLn
= do
649 | main <- UTCTime.getCurrentTime
650 | now "Current time:"
651 | Text.putStrLn IO.print now
652 |
653 | "ISO8601:"
654 | Text.putStrLn $ UTCTime.iso8601Show now
655 | Text.putStrLn
656 | "Parsed:"
657 | Text.putStrLn Maybe.maybe (Error.error "Impossible!") IO.print $ UTCTime.iso8601ParseM "2025-05-30T11:18:26.195147084Z"
658 |
659 | "Increased:"
660 | Text.putStrLn IO.print $ UTCTime.addUTCTime (Double.mult 60.0 60.0) now
661 |
662 | "Parts:"
663 | Text.putStrLn IO.print $ TimeOfDay.timeToTimeOfDay $ UTCTime.utctDayTime now
664 | IO.print $ UTCTime.utctDay now
Hell is a shell 108 | scripting language that is a tiny dialect of Haskell that I wrote for my 109 | own shell scripting purposes.
110 | 111 |#!/usr/bin/env hell
113 | = do
114 | main "Please enter your name and hit ENTER:"
115 | Text.putStrLn <- Text.getLine
116 | name "Thanks, your name is: "
117 | Text.putStrLn Text.putStrLn name
Overview
120 |fix
to do so.To read more, see my blog post about Hell.
134 | 135 | 142 | 143 |