Home Php C# Sql C C++ Javascript Python Java Go Android Git Linux Asp.net Django .net Node.js Ios Xcode Cocoa Iphone Mysql Tomcat Mongodb Bash Objective-c Scala Visual-studio Apache Elasticsearch Jar Eclipse Jquery Ruby-on-rails Ruby Rubygems Android-studio Spring Lua Sqlite Emacs Ubuntu Perl Docker Swift Amazon-web-services Svn Html Ajax Xml Java-ee Maven Intellij-idea Rvm Macos Unix Css Ipad Postgresql Css3 Json Windows-server Vue.js Typescript Oracle Hibernate Internet-explorer Github Tensorflow Laravel Symfony Redis Html5 Google-app-engine Nginx Firefox Sqlalchemy Lucene Erlang Flask Vim Solr Webview Facebook Zend-framework Virtualenv Nosql Ide Twitter Safari Flutter Bundle Phonegap Centos Sphinx Actionscript Tornado Register | Login | Edit Tags | New Questions | 繁体 | 简体


10 questions online user: 26

21
votes
answers
26 views
+10

Is there a way to get a Haskell setup on Windows without an installation? (Copy + paste)

I am programming on a school-given computer. I need to have a Haskell setup that does no installation (one I can drop on a flash drive and copy on to the C drive or just use from the flash drive perhaps).

I already have Eclipse up and running, so if I could get it on Eclipse without leaving any impact on the system past the Eclipse directory, that would be very good as well.

By no installation, I mean I have to be able to easily remove the program and roughly all traces without any uninstaller tool. All uninstaller tools are likely blocked.

What are my options?

up vote 17 down vote accepted favorite
沙发
+170
+50

Haskell Platform 的Windows安裝程序(我是維護者)有一個“便攜式”安裝“只是將文件提取到給定位置並且不觸及註冊表或任何系統設置的選項。您可以使用它將Haskell平台安裝到您的閃存驅動器,但您需要手動將GHC的位置添加到PATH。

如何在Windows機器上添加路徑? - PyRulez 2014年2月21日21:50

@PyRulez computerhope.com/issues/ch000549.htm - Mikhail Glushenkov 2014年2月22日19:08

那麼你必須改變計算機設置呢? - PyRulez 2014年2月22日19:23

如何使用GHCI 8.0.1進行便攜式安裝?當我下載它時,我得到的是一個.exe文件。運行時,它不提供便攜式安裝選項。謝謝。 - RussAbbott 2016年7月1日16:19

+40

我來自未來。我正在使用 Haskell Platform 的Windows安裝程序作為 Mikhail Glushenkov說

那麼您必須更改計算機設置嗎? - PyRulez

您還可以使用 CMD.exe SET 命令設置環境變量。(這些天他們教你的孩子是什麼?)一旦你設置了這樣的 PATH (或其他),那麼該會話和任何子會話的值都會持續存在。如果你從shell啟動一個IDE - 它應該有那些設置(除非有一個先前的實例......也許)所以它應該在你給出的約束內工作。我建議您將其保存到 .bat 文件

TL:DR ; 提取 bin旁邊的 Haskell平台創建 shell.bat / lib / 等並將其粘貼到其中;

  @ECHO OFF SET PATH =%~dp0 / bin;%PATH%CMD / K   

現在運行 shell.bat ,該提示符將能夠找到Haskell。因為您可以使用 shell.bat

沒有像舊學校的學校:) - wayneseymour 18年8月13日在15:50

0
votes
answers
26 views
+10

Nix頻道和GHC/Hackage包版本

2

我目前正在通過Gabriel's tutorialNix和Haskell上工作。Nix頻道和GHC/Hackage包版本

在尼克斯有channels和它們所包含 (類似棧LTS版本) 策展組Hackage包和GHC編譯器。

對於每個 LTS版本Stackage顯示了每個Hackage包的GHC編譯器版本和版本號 。在我開始使用頻道之前,我可以在哪裏查找nix頻道的這些信息?

沙发
0
0

對於Haskell軟件包的版本,我找到了一個答案:有像https://raw.githubusercontent.com/NixOS/nixpkgs/release-16.09/pkgs/development/haskell-modules/hackage-packages.nix這樣的文件。這些文件是不是真的好用...

編輯

在這個文件中,我可以 搜索= "base"那麼我就可以找到base庫的版本。 從基礎庫的版本我可以GHC版本lookup

這給下表:

newest -> base-4.10.0.0 -> GHC 8.2.1 
17.09 -> base-4.10.0.0 -> GHC 8.2.1 
17.03 -> base-4.9.1.0  -> GHC 8.0.2 
16.09 -> base-4-9.0.0  -> GHC 8.0.1 
16.03 -> base-4.8.2.0  -> GHC 7.10.3 
15.09 -> base-4.8.2.0  -> GHC 7.10.3 

但是一個簡單的方法將是不錯...

0
votes
answers
47 views
+10

如何爲Haskell語言的程序製作Makefile?

-2

我是Haskell語言中的新成員,我想爲現在正在處理的haskell程序創建一個makefile。但我不知道該怎麼做。如何爲Haskell語言的程序製作Makefile?

謝謝:)

+3

我們知道你想要什麼。大。你到目前爲止有什麼? – panther

+2

我不建議。而是使用'ghc --make',或'cabal'或'stack'。如果您確實需要'make',請閱讀有關它的文檔:https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/separate_compilation.html#using-make – chi

+1

那麼你首先想想你是什麼命令在製作程序時調用自己,然後將其轉換爲Makefile。 –

沙发
0
2

幾乎沒有哈斯克爾項目使用一個Makefile,因爲這是笨重,難以閱讀,而且容易出錯。

對於簡單的一次性項目,你真的不需要任何專門的編譯系統在所有:GHC本身可以計算出模塊之間的依賴關係:

[email protected]:/tmp$ cat >> Hello.hs 
module Main where 

import Greeting 

main = putStrLn greeting 

[email protected]:/tmp$ cat >> Greeting.hs 
module Greeting where 

greeting = "Hello, World!" 

[email protected]:/tmp$ ghc Hello.hs 
[1 of 2] Compiling Greeting   (Greeting.hs, Greeting.o) 
[2 of 2] Compiling Main    (Hello.hs, Hello.o) 
Linking Hello ... 

[email protected]:/tmp$ ./Hello 
Hello, World! 

...或者乾脆

[email protected]:/tmp$ runhaskell Hello.hs 
Hello, World! 

對於更復雜的項目,絕對是圖書館,你要使用Cabal。即您需要一個.cabal文件,例如對於上述「項目」 hello.cabal。可以通過cabal工具半自動生成這樣的文件:

[email protected]:/tmp$ mkdir hello 
[email protected]:/tmp$ mv Hello.hs hello 
[email protected]:/tmp$ mv Greeting.hs hello 
[email protected]:/tmp$ cd hello/ 
[email protected]:/tmp/hello$ cabal init 
Package name? hello 
This package name is already used by another package on hackage. Do you want to choose a different name? [default: y] n 
Package version? [default: 0.1.0.0] 
Please choose a license: 
    1) GPL-2 
    2) GPL-3 
    3) LGPL-2.1 
    4) LGPL-3 
    5) AGPL-3 
    6) BSD2 
* 7) BSD3 
    8) MIT 
    9) ISC 
    10) MPL-2.0 
    11) Apache-2.0 
    12) PublicDomain 
    13) AllRightsReserved 
    14) Other (specify) 
Your choice? [default: BSD3] 12 
Author name? [default: Justus Sagemüller] 
Maintainer email? [default: [email protected]] 
Project homepage URL? 
Project synopsis? hello 
Project category: 
* 1) (none) 
    2) Codec 
    3) Concurrency 
    4) Control 
    5) Data 
    6) Database 
    7) Development 
    8) Distribution 
    9) Game 
    10) Graphics 
    11) Language 
    12) Math 
    13) Network 
    14) Sound 
    15) System 
    16) Testing 
    17) Text 
    18) Web 
    19) Other (specify) 
Your choice? [default: (none)] 17 
What does the package build: 
    1) Library 
    2) Executable 
Your choice? 2 
What is the main module of the executable: 
* 1) Main.hs (does not yet exist, but will be created) 
    2) Main.lhs (does not yet exist, but will be created) 
    3) Other (specify) 
Your choice? [default: Main.hs (does not yet exist, but will be created)] 3 
Please specify? Hello.hs 
Source directory: 
* 1) (none) 
    2) src 
    3) Other (specify) 
Your choice? [default: (none)] 
What base language is the package written in: 
* 1) Haskell2010 
    2) Haskell98 
    3) Other (specify) 
Your choice? [default: Haskell2010] 
Add informative comments to each field in the cabal file (y/n)? [default: n] 

Guessing dependencies... 
Generating Setup.hs... 
Generating ChangeLog.md... 
Generating hello.cabal... 

You may want to edit the .cabal file and add a Description field. 

此時該文件將包含:

[email protected]:/tmp/hello$ cat hello.cabal 
-- Initial hello.cabal generated by cabal init. For further documentation, 
-- see http://haskell.org/cabal/users-guide/ 

name:    hello 
version:    0.1.0.0 
synopsis:   hello 
-- description:   
license:    PublicDomain 
author:    Justus Sagemüller 
maintainer:   [email protected] 
category:   Text 
build-type:   Simple 
extra-source-files: ChangeLog.md 
cabal-version:  >=1.10 

executable hello 
    main-is:    Hello.hs 
    -- other-modules:  
    -- other-extensions:  
    build-depends:  base >=4.10 && <4.11 
    -- hs-source-dirs:  
    default-language: Haskell2010 

這不是很完整,你仍然需要添加Greeting.hs到項目:(沒有文件擴展名)

... 
executable hello 
    main-is:    Hello.hs 
    other-modules:  Greeting 
    -- other-extensions: 
... 

但僅此而已,你現在可以做

[email protected]:/tmp/hello$ cabal build 
Resolving dependencies... 
Configuring hello-0.1.0.0... 
Preprocessing executable 'hello' for hello-0.1.0.0.. 
Building executable 'hello' for hello-0.1.0.0.. 
[1 of 2] Compiling Greeting   (Greeting.hs, dist/build/hello/hello-tmp/Greeting.o) 
[2 of 2] Compiling Main    (Hello.hs, dist/build/hello/hello-tmp/Main.o) 
Linking dist/build/hello/hello ... 

或者,只需運行腳本,cabal runcabal install以使其始終可用於您的系統。

0
votes
answers
36 views
+10

如何使用堆棧來安裝本地創作的Haskell模塊以實現全局使用?

1

我有一個本地創作Haskell的項目,這既產生:如何使用堆棧來安裝本地創作的Haskell模塊以實現全局使用?

  1. 二進制可執行文件,
  2. 幾個新的哈斯克爾模塊,我想發到我的其他訪問,哈斯克爾基礎,可執行文件。

後:

stack build 
stack install 

我發現:

  1. 二進制可執行文件(#1,以上)運行在任何目錄下就好了。
  2. 但是,新的Haskell模塊(上面#2)只有在我從我的項目目錄中運行時才能找到! (也就是說,對於除#1以外的任何可執行文件)。

我需要能夠從任何地方找到新模塊。 我該如何做到這一點?

+0

聽起來和[這個問題只有幾個小時前]類似(https://stackoverflow.com/questions/47989939/is-there-a-declarative-way-to-specify-packages-to-be-installed- into-global-proje),在那裏我建議你使用Cabal-install而不是stack,那麼你永遠不需要擔心模塊安裝是全局的。 – leftaroundabout

+0

感謝您的評論。是的,「cabal install」確實解決了我的問題。然而,現在我已經有了兩個單獨的,並行的,大部分冗餘的Haskell設備來啃硬盤空間,這看起來非常浪費和不必要。這尤其令人憤慨,因爲我生產的二進制可執行文件可以在任何目錄下運行,這意味着它知道如何從我的系統的任何地方(因爲它導入它們)找到我的新Haskell模塊。那麼,爲什麼我不能讓這些新模塊可用於其他Haskell可執行文件呢? – dbanas

+0

這絕對是不必要的,這就是爲什麼我在我的筆記本電腦上只使用_only_ Cabal-install,而僅使用Travis的Stack。 - 請注意,您的可執行文件找到導入模塊的方式與編譯器爲源文件找到它們的方式有很大不同。事實上,如果你靜態鏈接,那麼就不需要找到任何外部的東西,因爲一切都已經包含在二進制文件中了。如果您動態鏈接,它會查找特定散列動態庫文件的硬編碼路徑,但這隻適用於版本解析程序和鏈接程序在完成之前完成其工作。 – leftaroundabout

沙发
0
1

每個堆棧項目位於其自己的沙箱中,因此編譯的模塊只能在該項目中使用。編譯的依賴項(來自堆棧快照)有時會在項目之間共享。

請注意,您可以在軟件包列表中列出相對路徑,並指向該軟件包。它會重新構建,但可以通過這種方式直接在另一個項目中使用。爲什麼多餘的建築? Stack與Cabal-install有不同的項目模型 - 它不允許包DB的變化影響其他項目的構建。

共享這樣一個包的一個選擇是讓它在git倉庫中使用https://docs.haskellstack.org/en/stable/custom_snapshot/,但這些東西還是有點新的。

+0

感謝您的迴應。所以,如果我理解你在說什麼,那聽起來像爲Haskell可執行文件構建的模塊,使用動態插件的自定義風格,並不適合用堆棧構建/安裝。相反,它們應該用cabal製造/安裝。這是一個公平的總結嗎? – dbanas

+0

哦,我沒有意識到你正在嘗試動態加載模塊。如果您在項目中執行「堆棧生成pkg-x」,那麼這些模塊將在由'stack exec'設置的GHC_PACKAGE_PATH指定的包DB中可用。這聽起來像你想要更多的全球共享,但考慮到動態插件的工作,依賴版本需要排隊,所以它應該在一個堆棧項目內完成...... 不,你可能不應該使用cabal for這個。很確定新構建的東西實際上會讓這個更笨拙,因爲你需要指定一個特定的軟件包編號 – mgsloan

+0

謝謝!我想確保我明白了:所以,我真正需要做的是在將新的自定義插件添加到其堆棧項目描述之後,重新生成此「外部」可執行文件。是對的嗎?換句話說,嘗試構建可執行文件,使用一個堆棧項目和插件,使用不同的堆棧項目,這是一種固有的缺陷。如果這是正確的,那麼潛在的插件開發人員似乎非常脆弱和限制。我認爲這是一個設計良好的/有記錄的API(可執行文件)旨在防止的。我必須在這裏錯過一些基本的東西。 – dbanas

0
votes
answers
23 views
+10

如何合併兩個字符串我Haskell並返回一個新的字符串

-3

如何合併兩個字符串。例如,我有兩個列表
["me","you","he"]["she","they","it"]。我想要形成一個新的列表,其中每個對應的字符串組合在一起,如 ["meshe","youthey","heit"]。現在的問題是:我怎麼能合併兩個字符串如何合併兩個字符串我Haskell並返回一個新的字符串

+0

也可以'結合xs ys = [x ++ y | (x,y)< - zip xs ys]' – RoadRunner

沙发
0
5
combine = zipWith (++) 

zipWith需要兩個列表,並應用於給兩個列表的第一個項目,然後是第二等的功能。如果一個表比其他長,其額外項目將被跳過。

++函數帶兩個列表並將它們連接在一起。一個字符串只是一個字符列表。

"hello " ++ "world" == "hello world"

用法:

λ> combine ["me","you","he"] ["she","they","it"] 
["meshe","youthey","heit"] 
λ> combine [] [] 
[] 
λ> combine ["me", "you"] ["she"] 
["meshe"] 
λ> 

++操作是很基本的,所以你可能會更好繼續讀你來之前計算器正在使用的任何學習材料,你就會有我期望的很多問題都會在你的書中得到解答。

如果你不想使用zipWith,你可以很簡單地用遞歸寫像這樣:

combine [] _ = [] 
combine _ [] = [] 
combine (x:xs) (y:ys) = (x ++ y) : combine xs ys 

用法和以前一樣。

+0

如果不想使用zipwith函數 –

+0

@UmairAziz請參閱我的編輯。這是一個非常簡單的遞歸函數。 – Zpalmtree

+0

@UmairAziz你爲什麼不呢?作爲一般規則,您應該儘量避免顯式遞歸;這是您重塑車輪的標誌。 – chepner

0
votes
answers
43 views
+10

哈希克爾派生和實例之間的區別

2

我有一個類型,我已經做了Show類的實例,但我沒有得到期望的結果。如果我嘗試使用deriving字,然後它工作,使之秀類的實例,但如果嘗試像:哈希克爾派生和實例之間的區別

instance Show (SomeValue v) where 
    show (Null) = "You have no value" 
    show (Justs v) = show (Justs v) 

findKey key = foldr ((k,v) acc -> if key == k then Justs v else acc) Null 

它去在未結束循環(種)。我認爲,通過使用deriving單詞得到的默認實現工作,上面的代碼有什麼問題?它編譯但它不打印任何東西。

是否有可能我打印這樣的值:(沒有「Justs」)?

instance Show (SomeValue v) where 
    show (Null) = "You have no value" 
    show (Justs v) = show (v) 
+0

相關:https://stackoverflow.com/questions/28665917/accessing-the-default-show-in-haskell –

+0

@WillemVanOnsem它不一樣。對於我的數據構造函數,我只有兩個值,對於這兩個值我已經定義了show,但它不打印任何東西,我甚至嘗試使用type-annotations進行讀取,但它不起作用:) –

+0

當然,它確實不打印任何東西,它被卡在無限循環中。 –

沙发
0
3

代碼

show (Justs v) = show (Justs v) 

進入一個無限循環出於同樣的原因

f x = f x 

一樣。

你可以把它寫不Justs爲你問,你只需要一個約束的情況下


instance (Show v) => Show (SomeValue v) where 
    show Null = "You have no value" 
    show (Justs v) = show v 

,因爲如果你要儘量表現出vv應該showable,不是嗎?

0
votes
answers
26 views
+10

錯誤處理多個要麼值

1

我有兩個either值,例如:錯誤處理多個要麼值

Either String Config -- error string or config parsed 
Either String Env  -- error string or environment variables detected 

而且我想他們的價值觀提取到這個紀錄:

type App = App { config :: Config, env :: Env } 

而失敗快,如果有是錯誤(其中一個值爲Left)。

我可以使用兩個case語句,但我想知道是否已經有一個我可以在此使用的抽象了?

理想情況下,我會在出現錯誤時記錄消息並立即退出程序。

沙发
0
6

對於這樣的事情,您可以使用Either StringApplicative的事實。

假設

mcnf :: Either String Config 
menv :: Either String Env 

你可以寫

mapp :: Either String App 
mapp = App <$> mcnf <*> menv 
+1

感謝。另外我用'System.Exit.die'來處理錯誤:https://github.com/srid/slownews/commit/a80134c4d918e8b258ae3f1115a62c99ae9b601f –

+0

聽起來很對! – gallais

0
votes
answers
20 views
+10

Haskell中的「隱形」函數參數如何工作?

3

我所談論的一個例子是takeWhileHaskell中的「隱形」函數參數如何工作?

takeWhile :: (a -> Bool) -> [a] -> [a] 

用法示例

takeWhile (< 3) [1,2,3,4,1,2,3,4] == [1,2] 

從我可以告訴(< 3)成爲(a < 3)其中a是要檢查在列表中的當前項目。

這是如何在Haskell做,我會怎麼能夠移動其中a去,所以我可以做類似

takeWhile ((length a) < 4) ["aaa", "aaaaa"] 
+3

如果你正在尋找谷歌果汁:這種語法,你省略了符號中綴運算符的一個操作數,稱爲_section_。 '(<3)'表示'( x - > x <3)'和'(「abc」++)'表示'( x - >「abc」++ x)'。 –

沙发
0
9

(< 3)被稱爲「部分」,僅適用於中綴操作符。這是編寫縮寫函數的語法糖。 (< 3)相當於x -> x < 3(3 <)相當於x -> 3 < x

因此,(< 3)是返回Bool的一個參數的函數。這正是takeWhile的期望。

隨着length,你必須寫在全面的功能:

takeWhile (x -> length x < 4) ["aaa", "aaaaa"] 

或定義自己的功能,您可以咖喱:

shorterThan n x = length x < n 

takeWhile (shorterThan 4) ["aaa", "aaaaa"] 

如果你喜歡冒險,你可以寫

takeWhile ((< 4) . length) ["aaa", "aaaaa"] 

或者可能是以更可讀的方式

(??) = flip (.) 

takeWhile (length ?? (< 4)) ["aaa", "aaaaa"] 
+0

Haskell已經訓練了我很多東西來閱讀從右到左的函數組成,因此'''版本對我來說可讀性較差。 – 4castle

+0

@ 4castle這是一個作文,但不要這樣讀。考慮到 ??作爲變量的佔位符。 「東西的長度小於四」。 –

板凳
0
3

有無處可去,你要記住,在Haskell每個功能currified,所以,想在(+)功能,可以做下,想在第一類型:

(+) :: Num a => a -> a -> a 

現在我可以做我的自定義Plusone精選,plusTwo等,如此之快得益於curryfication,因爲

(+) :: Num a => a -> a -> a 

有 「隱paranthesis」,真的是這樣的:

(+) :: Num a => a -> (a -> a) 

意思給我一個號碼給你一個功能回來,所以:

plusOne :: Num a => a -> a 
plusOne = (+1) 

plusTwo :: Num a => a -> a 
plusTwo = (+2) 

你能看到什麼?你給予它只是兩個參數中的一個變換的(+)在一個新的功能,也會有相同的<>功能,您可以創建功能greaterThanTen是這樣的:在你的takeWhile (< 3)例如

greaterThanTen :: (Num a, Ord a) => a -> a 
greaterThanTen = (>10) 

所以你(< 3)是功能「小於3」,如果你「讀」所有的功能將是「拿n而小於3」

你可以在控制檯通過詢問類型:t命令

:t (< 3) 
(< 3) :: (Num a, Ord a) => a -> Bool 
+2

但請注意'(> 10)4'與'(>)10 4'完全不同。雖然currying/partial應用程序可以讓我們修正第一個參數,但section的語法允許使用'(10 <)和'(10 <)' - 這不僅僅是在工作中起作用,它還接受編譯器的特殊解析和處理。 – chi

+0

@chi非常感謝你,像你這樣的專家,我每天都會學到新東西:),就像......昨天!如果我的記憶沒有失敗,非常感謝評論和時間 –

地板
0
3

takeWhile的第一個參數是謂詞;這是您的一元函數(< 3),當參數具有數值語義並且小於3時,這是正確的。這個選項適用於每個列表成員,當謂詞首次不成立時,這個選項組成一個新列表並終止。已通過的元素組成結果列表。

你的例子幾乎是正確的,你只能將它轉換成謂詞形式,即創建一個一元函數,當參數(字符串)的長度小於4時,這是真實的。這可以通過例如

(a -> (length a) < 4) 

然後

takeWhile (a -> (length a) < 4) ["aaa", "aaaaa"] 

應該做你的期望。

0
votes
answers
0 views
+10

How to call impure functions from pure ones?

I have just finished reading a "Learn You a Haskell for Great Good!" book so my question can be very naive. What I don't understand is how to call "impure" IO functions from the pure code.

Here is a working example written in C#. In our business logic we plan some actions based on weather. We do it in usual C# manner.

interface IWeatherForecast
{
    WeatherData GetWeather(Location location, DateTime timestamp);
}

// concrete implementation reading weather from DB
class DbWeather : IWeatherForecast
{
    public override WeatherData GetWeather(Location location, DateTime timestamp)
    {...}
}

class WeatherFactory
{
    public IWeatherForecast GetWeatherProvider()
    {...}
}

// Business logic independent from any DB
class MaritimeRoutePlanner
{
    private IWeatherForecast weatherProvider = weatherFactory.GetWeatherProvider();

    public bool ShouldAvoidLocation(Location location, DateTime timestamp)
    {
        WeatherData weather = weatherProvider.GetWeather(location, timestamp);
        if(weather.Beaufort > 8)
            return true;
        else...
            ...
    }
}

How do I implement this logic in Haskell?

In reality "pure logical" MaritimeRoutePlanner calls weatherProvider.GetWeather() which is "impure IO" stuff.

Is it possible in Haskell? How would you model this in Haskell?

沙发
+110

一般问题(如何从纯函数调用不纯函数)是一个FAQ。请参阅此问题及其答案:如何从不纯的方法返回纯值

以更具功能性的方式构造代码取决于与软件架构相关的任何其他主题,在某些情况下。你在写什么样的节目?REST API?智能手机应用?控制台程序?批量工作?一个加载项?

在很多情况下,你可以使用我称之为不纯净的不纯三明治

  1. 收集来自不纯来源的所有必需数据
  2. 使用该数据调用纯函数
  3. 使用纯函数的返回值做一些不纯的东西

    在Haskell中你可以这样做,因为入口点总是不纯的。这是天气决策问题的简单草图。首先定义您将使用的数据。在这里,我只包含 beaufort 值,但我认为 WeatherData 会包含更多数据(这就是我将其定义为 data 的原因)而不是 newtype )。

      data WeatherData = WeatherData {beaufort :: Int} derived(Eq,Show)  

    您现在可以将决策逻辑编写为纯函数:

      shouldAvoidLocation :: WeatherData  - &gt; Bool shouldAvoidLocation天气= beaufort天气&gt; 8   

    加载数据是一个完全具体的操作:

      readWeatherFromDb :: Location  - &gt; LocalTime  - &gt; IO WeatherData readWeatherFromDb location timestamp =  - 实现在这里......   

    这里没有明确的抽象。此函数读取数据并返回不纯的数据。这可能是 impure-pure-impure sandwich 中的第一个(不纯的)步骤。

    现在可以根据该架构构建应用程序的入口点:

      main :: IO()main = do w&lt;
         
    			
            

这里的问题是我们无法提前收集所有数据。这应该是一个潜在的位置深深地埋藏在逻辑中,并且可能是根本不应该调用航空位置 - 例如港口拒绝所有船只,所以我们甚至不需要天气数据。这就是我们需要内部数据的原因,因为业务逻辑决定了我们在每种特定情况下需要哪些数据。 - Stefan Dorn 8月29日7:48

@StefanDorn首先要做的事情是:如果出于某种原因,不可能存在纯净不纯的三明治,那么就有办法模拟纯粹的互动。 - Mark Seemann 8月29日8:13

@StefanDorn然而,这是一个明确考虑架构可以帮助的案例。我不知道你想开发的软件的细节,但我经常听到那种反驳的论点。然而,经常会发现数据很小或者无论如何都要缓存,在这种情况下,即使并不总是使用它也可能不会有问题。毕竟,您通过严格遵守按需数据来解决什么问题? - Mark Seemann 8月29日8:18

我会这样说 - 业务逻辑的算法依赖于来自IO的数据(在这种情况下是DB)。if(get IO a == something)then if(get IO b == something)then if(get IO c == something)...并且它嵌套了许多级别。每个步骤都取决于DB的一些数据。 - Stefan Dorn 8月29日10:42

@StefanDorn我理解这一点,但通常情况下,只是批量获取所有数据可能仍然更有效。在这个特殊的场合,我可能也错了。我从未对气象数据做过任何工作,但我认为数据集可能真的非常大。在这种情况下,我会按照上面的链接找到一个免费的monad。 - Mark Seemann 8月29日10:48

板凳
+60

简而言之,您不会从不纯的“功能”(又名动作)中提取数据; 你的纯函数推送到一个新的动作。

  data WeatherData = WeatherData {beaufort :: Int,...}  -  getWeather是一个纯函数 - getWeather someLocation someDate是一个动作getWeather :: Location  - &gt; 日期时间 - &gt; IO WeatherData getWeather ld = ...  -  badWeather是一个纯函数badWeather :: WeatherData  - &gt; Bool badWeather wd = beaufort wd&gt; 8  -  avoidLocation是一个纯函数 -  avoidLocation someLocation someDate是一个动作 - 我们可以简单地使用fmap将纯函数badWeather提升(或换行)成一个新动作。avoidLocation :: Location  - &gt; 日期时间 - &gt; IO Bool avoidLocation ld = fmap badWeather(getWeather ld)  

avoidLocation 实际上不会产生布尔值; 它会创建一个动作,当最终执行时,使用 badWeather 生成一个布尔值。

原始代码中的抽象也可以通过类型别名轻松包含:type WeatherSource = Location - > DateTime - > IO WeatherData。然后getWeatherProvider可能是一个IO WeatherSource,或者更好,一个纯函数,如Options - > WeatherSource,它可能会返回dbWeather或其他函数。当您向IWeatherForecast添加方法时,可以将WeatherSource设置为数据类型并向其添加字段。 - Jon Purdy 8月30日17:27

地板
+30

如果效果和纯逻辑之间的交织对于基于“三明治”的解决方案而言过于复杂,一个选项是使用发生其效果的monad参数化您的依赖关系,然后使您的逻辑多态< / em> over all monads。

例如,这里是代码的近似翻译:

  { -  #LANGUAGE ExplicitForAll# - } dataDataData = WeatherData  -  dummy type data Location = Location  - 虚拟类型数据DateTime = DateTime  - 虚拟类型newtype WeatherForecast m = WeatherForecast {getWeather :: Location  - &gt; 日期时间 - &gt; m WeatherData}  - 只是一个创建预测类型的monadic动作WeatherFactory m = m(WeatherForecast m) - 在IO monad中工作的具体工厂aWeatherFactory :: WeatherFactory IO aWeatherFactory = do putStrLn“我正在有效地分配WeatherForecast !” 返回WeatherForecast {getWeather =  _ _  - &gt; 做putStrLn“我正在连接互联网!” 返回WeatherData} newtype MaritimeRoutePlanner m = MaritimeRoutePlanner {shouldAvoidLocation :: m Bool}  - 逻辑只知道m是monad而已。makeMaritimeRoutePlanner :: forall m。Monad m =&gt; WeatherFactory m  - &gt; MaritimeRoutePlanner m makeMaritimeRoutePlanner forecastFactory = MaritimeRoutePlanner {shouldAvoidLocation = do forecast&lt;  -  forecastFactory WeatherData&lt;  -  getWeather forecast Location DateTime return False}   

WeatherForecast WeatherFactory 具有monad的类型参数,其方法具有效果。特别是, aWeatherFactory 返回一个 WeatherFactory ,它可以通过 IO 工作。

但请注意 forall makeMaritimeRoutePlanner 的签名中。它迫使逻辑在所有可能的monad上工作,这意味着它不能使用特定于任何具体 monad的功能。

使用示例:

  * Main&gt; 让planner = makeMaritimeRoutePlanner aWeatherFactory * Main&gt; shouldAvoidLocation planner我正在有效地分配WeatherForecast!我正在连接互联网!False   

将有效的依赖项作为参数(或 Reader monad的环境)传递是相对常见的。我认为,在monad上制作逻辑多态的进一步技巧不太受欢迎。最终,生活在 IO 可能太方便了,或者至少没有足够的问题来打扰“多面性的面纱”。

(当然,

还有一个mtl风格的选项,你可以在这里提供Monad m => WeatherMonad m ......这里提供了获取天气数据但不能执行任意IO的能力。这种方法对于模拟特别有用。 - dfeuer 8月29日19:14

@dfeuer是的,我得到的印象是IO中的函数记录,免费/更自由和类似MTL的方法在实践中比这种多态技巧更多地使用。我确实喜欢它,至少在概念上,因为它感觉像是IO中函数记录的某种“自然”演变。 - danidiaz 8月29日20:13

在我看来,“工厂”方法只有在有两个不同的monad时才有意义:m(WeatherForecast n),将连接到天气服务器的效果与检索预测的事件分开。 - dfeuer 8月29日20:20

17
votes
answers
8 views
+10

Can we write a function in Haskell to measure the number of bytes that a string of n characters occupies?

Suppose I have the string

"abc"

and I want to calculate the number of bytes it occupies in memory.

I could do:

import Data.Bits
finiteBitSize ("abc" :: [Char])

but that breaks because [Char] is not a type supported by the function. (Also it is bits not bytes, but the point was to paint a picture of what I'm looking for).

My question is: Can we write a function in Haskell to measure the number of bytes that a string of n characters occupies?

沙发
+40
+50

有一个名为 ghc-datasize 的Haskell包,它允许您按每个Haskell值计算占用的内存。您甚至可以在官方Hackage文档中看到如何计算字符串大小的示例:

+130

这很复杂。

让我们来谈谈GHC,特别是关于 String ,让我们假设这个东西已被完全评估,所以我们没有迭代地使用它以GC友好的方式,我们没有延迟评估并存储代表巨大数据结构的微小thunk。

在完成所有这些简化假设之后,我们需要知道一些定义。

  type String = [Char] data [a] = [] | a:[a]  -  pseudosyntax数据Char = C#Char# - 我在猜测,找不到规范来源  

现在我们要使用一些规则拇指。首先:未装箱的东西(如 Char#)通常存储在机器字中。我们生活在64位机器的世界中,所以 Char#可能是8个字节,即使它可能只使用它的底部4个字节。第二:数据构造函数是一个单词,用于说明哪个构造函数,以及指向每个字段的单词。

现在我们已经准备好了它。

空字符串是 [] ,构造函数一个字,字段没有字,所以总共一个字。

非空字符串是 c:cs ,所以一个字对于构造函数,一个单词指向 c ,一个单词指向 cs ,一个单词指向 C# constructor, Char#的一个单词。这是5个字加上我们需要的 cs < / code>。

因此,对于长度为n的 String ,我们有5 * n个单词来表示 String 的主体和一个额外的终止 [] 实际上每个字符有40个字节!Yikes。

现在您知道为什么像 Text (或者,在适当的情况下, ByteString )这样的压缩表示是如此重要。

Char的定义与GHC.Types中的定义非常相似。唯一的额外位是CTYPE注释。 - dfeuer 8月31日17:22

GHC是否以小Int值的方式预分配常见的ASCII Char值? - dfeuer 8月31日17:29

@dfeuer啊,我敢打赌!我打赌这个的原因是我发现另一个问题(我现在很难找到),与我对最大居住点的观察结果相比,在这个答案中构建的估计值太高了; 分享小Chars或许可以解释这种差异。 - Daniel Wagner 8月31日17:51

@dfeuer一些简单的测试似乎支持这个假设:readFile“foo”>> = s - > print(长度s)>> print(长度s)大约是最大驻留时间的两倍,如果foo中有'1114111'而不是它有'是的。 - Daniel Wagner 8月31日17:55

是的,ghc会预先分配ASCII字符并确保在GC期间共享。那个和小的Int技巧都是从hbc继承的东西。 - 8天前的奥古斯都