In the Haskell Weekly Newsletter today, there was a link to this article: https://www.scannedinavian.com/how-to-run-haskell-source-files-like-shell-scripts.html

In it, the author shows two ways to run Haskell source files as a script. The first using runhaskell:

#!/usr/bin/env runhaskell
main = print "Hello World!"

And the second using cabal:

#!/usr/bin/env cabal
 
{- cabal:
build-depends: base
  , hedgehog
-}
{-# LANGUAGE OverloadedStrings #-}
 
module Main where
 
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
 
prop_associative_float :: Property
prop_associative_float = property $ do
  a <- forAll $ Gen.float (Range.linearFrac (-100) (100))
  b <- forAll $ Gen.float (Range.linearFrac (-10000) (10000))
  c <- forAll $ Gen.float (Range.linearFrac 5 1000000)
  ((a * b) * c) === (a * (b * c))
 
prop_reflexive :: Property
prop_reflexive = property $ do
  a <- forAll $ Gen.float (Range.linearFrac (-100) (100))
  (a / 0) === (a / 0)
 
main = do
  checkParallel $
    Group "Example" [ ("prop_reflexive", prop_reflexive), ("prop_associative_float", prop_associative_float) ]

Beforehand, I was only aware of how to do it with stack.