Строчная интерполяция в Haskell

Я хочу написать код, который будет выводиться:

length [1,2,3] => 3   

В Ruby я мог бы сделать это как:

puts "length [1,2,3] => #{[1,2,3].length}"

После попытки Haskell не удалось выполнить...

Prelude Data.List> print "length [1,2,3]"
"length [1,2,3]"
Prelude Data.List> print (length [1,2,3])
3
Prelude Data.List> print "length [1,2,3]" (length [1,2,3])

<interactive>:1:0:
  Couldn't match expected type `Int -> t'
     against inferred type `IO ()'
 In the expression: print "length [1,2,3]" (length [1, 2, 3])
 In the definition of `it':
      it = print "length [1,2,3]" (length [1, 2, 3])
 Prelude Data.List>

Ответы

Ответ 1

Строки - это действительно просто списки. Таким образом, вы можете преобразовать число, возвращаемое по длине, и добавить его в свою другую строку с обычными функциями списка:

print $ "length [1,2,3] " ++ show (length [1,2,3])

Ответ 2

В то время как в других плакатах упоминаются многие "правильные" способы интерпретации строк, есть более удобный способ использования квазиковации и библиотека интерполированной строки-perl6:

{-# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-}

import Text.InterpolatedString.Perl6 (qq)

main = putStrLn [$qq| length [1,2,3] => ${length [1,2,3]} |]

На самом деле существует также библиотека interpolatedstring-qq, которая предлагает синтаксис Ruby.

{-# LANGUAGE QuasiQuotes, ExtendedDefaultRules #-}

import Text.InterpolatedString.QQ (istr)

main = putStrLn [$istr| length [1,2,3] => #{length [1,2,3]} |]

Тем не менее, вы, вероятно, должны просто использовать show и ++ или concat для склеивания строк

main = putStrLn $ "length [1,2,3] => " ++ show (length [1,2,3])

или

main = putStrLn $ concat ["length [1,2,3] => ", show $ length (1,2,3)]

Последний имеет тенденцию выглядеть лучше, по коду, когда вы склеиваете много фрагментов строки.

Ответ 3

Вы также можете просто использовать Text.Printf, который включен в базовые библиотеки GHC:

> let format s = printf "length %s => %d\n" (show s) (length s)
> format [1,2,3]
length [1,2,3] => 3

В Hackage http://hackage.haskell.org существует несколько пакетов интерполяции строк, если вам нужны более благоприятные ситуации.

Ответ 4

Используйте format функцию text-format-simple библиотека:

import Text.Format
format "length [1,2,3] => {0}" [show $ length [1,2,3]]

Ответ 5

Попробуйте это в ghci:

Prelude> :t print
print :: (Show a) => a -> IO ()

Как вы можете видеть, функция print принимает только один аргумент, а код выше - два.

Вместо этого попробуйте следующее:

putStrLn ("length [1,2,3] => " ++ show (length [1,2,3]))

Он соединяет две строки с ++, а затем печатает ее.

Ответ 6

Вы можете использовать что-то вроде

putStr "length [1,2,3] => "
print (length [1,2,3])

EDIT:

Если вы хотите сделать это как функцию, передать любой список и записать его длину, вы можете сделать это следующим образом:

print_length :: Show a => [a] -> IO ()
print_length xs = print ("length " ++ show xs ++ " => " ++ show (length xs))

Main> print_length [1,2,3,4,5]
"length [1,2,3,4,5] => 5"

Main> print_length []
"length [] => 0"

Конечно, как указано выше, вы можете использовать putStrLn вместо печати. ​​

Ответ 7

В дополнение к тому, что другие сказали, вы также можете использовать оператор monadic bind >> для объединения двух операций ввода-вывода:

putStr "length [1,2,3]: " >> print (length [1,2,3])

Это эквивалентно объединению их с do-notation:

do putStr "length [1,2,3]: "
   print (length [1,2,3])

Ответ 8

если вы просто хотите отлаживать такие вещи, как:

inc :: Int -> Int
inc x = [debug|1+x|]

inc 2
-- prints "1+x = 3"
-- returns 3

этот QuasiQuoter может помочь:

{-# LANGUAGE TemplateHaskell #-}
module Template where
import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax
import Language.Haskell.Meta.Parse
import Debug.Trace

-- | e.g. f x = [debug|1+x|]
debug :: QuasiQuoter
debug = QuasiQuoter traceExp undefined undefined undefined

-- | e.g. traceExp "1+x"  ->  [| trace "1+x = $([|1+x|])" (1+x) |]
--   (or something)
traceExp :: String -> Q Exp
traceExp s = [|( trace $(showExp s) $(parseE s) )|]

-- | e.g. showExp "1+x"  ->  [| "1+x" ++ " = " ++ show (1+x) |]
showExp :: String -> Q Exp
showExp s = [|( $(stringE s) ++ " = " ++ show $(parseE s) )|]

-- | e.g. parseE "1+x"  ->  (UInfixE (LitE (IntegerL 1)) (VarE +) (VarE x))
parseE :: String -> Q Exp
parseE = return . either (const undefined) id . parseExp

-- $ cabal install haskell-src-exts
-- $ cabal install haskell-src-meta

переменные захватываются динамически. Я написал это, изучая Template Haskell, но debug работает для отладки материала во время разработки.

Ответ 9

Недавно я опубликовал fmt библиотеку форматирования в моем ответе на другой вопрос. С помощью этой библиотеки вы можете написать интерполяцию следующим образом:

> "There are "+|n|+" million bicycles in "+|city|+"."

Хотя, вас также может заинтересовать какая-то библиотека TH с Quasi Quoters:

https://hackage.haskell.org/package/interpolate-0.1.1/docs/Data-String-Interpolate.html

Хотя, эта библиотека очень медленная по сравнению с fmt.