1

I've generated a list of assertions I'd like to have run.

Here's an interesting case. In the script below, a list of pairs of strings are generated with the entries in the pairs never being equal (namePairs). An assertion should throw an error if one of the strings is a substring of the other string in the pair. I've played around with the script enough to notice that it will throw an error if you encounter foo (which happens when head is used, as shown), or an error will be shown if you replace head with last, but in this case it will occur for bar - the last string in the list). But this only works if I leave off the i =/ j guard, which is not what I want, but helped me to identify the issue. If the guard is left in, then no error is thrown since these cases were the cases where i == j.

I saw How to force evaluation in Haskell?, but despite using the BangPatterns approach described there, I've not been able to get this to work, despite the cargo-cult level of usage.

#!/usr/bin/env stack
{- stack script --nix --resolver lts-14.20
  --nix-packages zlib
  --no-nix-pure
  --package non-empty-text
  --package text
  --package time
-}

{-# LANGUAGE BangPatterns        #-}
{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Main where

import           Data.Maybe
import           Data.List (isSubsequenceOf)
import           Data.Text (Text)
import qualified Data.Text as DT
import qualified Data.Text.Lazy as DTL
import qualified Data.Text.IO as DTIO

entryKeys :: [Text]
entryKeys = filter (\t -> DT.length t > 0) $ DT.split (==' ')
  "foo DIBBsMain DIBBsContainers DIBBs bar"

main :: IO ()
main = do
  putStrLn $ show entryKeys
  !_ <- entryNameCheck entryKeys
  pure ()

entryNameCheck :: [Text] -> IO ()
entryNameCheck !eNames = do
  putStrLn $ show namePairs
  !x <- pure $ checkPair <$> namePairs
  pure $ head x
  where
    !lStr = DT.unpack . DT.toLower
    !namePairs = [(lStr i, lStr j) | i <- eNames, j <- eNames, i /= j]
    errmsg !ns = "!! " <> fst ns <>  " is a substring of " <> snd ns
    checkPair !p = assertOrErr ((not $ isSubsequenceOf (fst p) (snd p))) (errmsg p)

assertOrErr :: Bool -> String -> ()
assertOrErr !cond !msg = if cond then () else error msg
bbarker
  • 11,636
  • 9
  • 38
  • 62
  • 3
    I think the approach taken makes things more difficult than necessary. You are working mostly in pure code, and yet your types are not expressive. `Bool -> String -> ()` doesn't reflect the possibility of failure. Instead, you carefully mold the evaluation order so that errors are thrown at the correct time during execution. But lazy evaluation is wobbly and difficult to shape. I would advise either to remain pure and use `Either` instead of `error`, actually reflecting the failure in the types, or to move to a more `IO` based solution which used exceptions. `IO` sequencing is more predictable – danidiaz Apr 24 '20 at 19:57
  • It's a fair point. Normally I would have gone with the Either approach from the start and looks like I may need to. This started off as a small Haskell script, and kept growing into slightly larger incantations of its former self. – bbarker Apr 24 '20 at 20:07

0 Answers0