2008-12-01 38 views
100

我一直是一個Web開發人員,現在已經開始學習一些函數式編程。和其他人一樣,我將這些概念中的許多應用於我的專業工作方面遇到了一些重大困難。對我來說,主要原因是我看到FP之間的無狀態目標之間的矛盾似乎與我所做的大多數Web開發工作都與數據庫密切相關,這些數據庫非常以數據爲中心。數據庫和函數式編程有何不同?

一兩件事讓我更富有成效開發商對事物的OOP側像MyGeneration d00dads對於.NET,Class對象關係映射器的發現:: DBI對Perl,ActiveRecord的紅寶石,等這使我不用花一天時間寫插入和選擇語句,並且專注於將數據作爲對象輕鬆處理。當然,我仍然可以在需要它們的權力時編寫SQL查詢,但否則它會在幕後很好地抽象出來。

現在,轉向功能性編程,似乎像許多FP Web框架一樣,Links需要編寫大量的樣板sql代碼,如this example。網絡鎖看起來好一點,但它似乎使用了一種OOP模型來處理數據,並且仍需要爲數據庫中的每個表手動編寫代碼,如this example。我想你使用一些代碼生成來編寫這些映射函數,但是這看起來顯然是不喜歡的。 (注意:我沒有非常仔細地查看Weblocks或Links,我可能會誤解它們是如何使用的)。

所以現在的問題是,對數據庫訪問的部分(我認爲是相當大的)Web應用程序,或其他發展需要的接口與SQL數據庫中,我們似乎壓低以下路徑之一:

  1. 不要使用函數式編程
  2. 訪問數據在一個惱人的,非抽象的方式,涉及手動編寫大量SQL或類似SQL代碼ALA鏈接
  3. 隊我們的功能語言爲僞OOP範式,從而消除了真正的函數式編程的一些優雅和穩定性。

顯然,這些選項都不理想。是否找到了解決這些問題的方法?這裏真的有一個問題嗎?

注:我個人最熟悉的FP前LISP,所以如果你想給任何例子,知道多FP語言,口齒不清很可能是選擇的首選語言

PS:具體問題到web開發的其他方面,請參閱this question

+0

另請參閱:http://stackoverflow.com/questions/218190/is-functional-to-relational-mapping-easier-than-object-to-relational – 2008-12-01 09:32:16

+0

@WW - 謝謝,不知何故錯過了這個問題。 – 2008-12-01 09:40:46

+4

查看ClojureQL和HaskellDB。它們是利用關係代數的抽象層。 – Masse 2011-08-05 11:34:51

回答

39

首先,我不會說CLOS(Common Lisp Object System)是「pseudo-OO」。這是一流的OO。

其次,我相信你應該使用適合你需求的範例。

你不能無狀態地存儲數據,而函數是數據流,並不真正需要狀態。

如果你有多種需求混合,混合你的範例。不要只限於使用工具箱的右下角。

10

完全沒有。有一種稱爲「功能數據庫」的數據庫類型,其中Mnesia可能是最易於訪問的示例。基本原則是函數式編程是聲明式的,所以它可以被優化。您可以在持久集合上使用List Comprehensions實現連接,查詢優化器可以自動計算出如何實現磁盤訪問。

Mnesia寫在Erlang和至少有一個網絡框架(Erlyweb)可用於該平臺。 Erlang本質上與無共享線程模型並行,因此它在某些方面適用於可伸縮體系結構。

13

我不認爲fp語言的無狀態性是連接數據庫的問題。 Lisp是一種非純粹的函數式編程語言,所以它不應該有任何處理狀態的問題。像Haskell這樣的純函數式編程語言可以處理可以應用於數據庫的輸入和輸出。

從你的問題看來,你的主要問題似乎在於找到一種很好的方法來將你從數據庫中得到的基於記錄的數據抽象成lisp-y(lisp-ish?)而不必寫很多SQL代碼。這似乎更像是工具/庫的問題,而不是語言範例的問題。如果你想做純FP,lisp可能不適合你。 Common Lisp似乎更多的是關於從oo,fp和其他範式中整合好的想法,而不是純粹的fp。如果你想要去純FP路線,也許你應該使用Erlang或Haskell。

我確實認爲lisp中的'僞oo'思想也有其優點。你可能想嘗試一下。如果它們不適合您想要處理數據的方式,您可以嘗試在網絡鎖之上創建一個圖層,以便您按照自己想要的方式處理數據。這可能比自己寫一切都容易。

聲明:我不是Lisp專家。我主要對編程語言感興趣,並且一直在使用Lisp/CLOS,Scheme,Erlang,Python和一些Ruby。在日常編程生活中,我仍然不得不使用C#。

71

從數據庫人的角度來看,我發現前端開發人員試圖找到使數據庫適合他們的模型的方法,而不是考慮使用不是面向對象或功能的數據庫的最有效方法但關係和使用集合論。我看到這通常會導致代碼執行不良。此外,它會創建難以調整性能的代碼。

當考慮數據庫訪問時,主要有三個考慮因素 - 數據完整性(爲什麼所有業務規則應該在數據庫級別執行而不是通過用戶界面),性能和安全性。編寫SQL是爲了比前端語言更有效地管理前兩種注意事項。因爲它是專門設計來做到這一點的。數據庫的任務遠遠不同於用戶界面的任務。難怪在管理任務中最有效的代碼類型在概念上是不同的?

數據庫擁有對公司生存至關重要的信息。難怪企業在生存受到威脅時不願嘗試新方法。很多企業都不願意升級到現有數據庫的新版本。所以在數據庫設計中存在內在的保守性。這是故意的。

我不會嘗試編寫T-SQL或使用數據庫設計概念來創建用戶界面,爲什麼要嘗試使用界面語言和設計概念來訪問我的數據庫?因爲你認爲SQL不夠花哨(或新)不夠?或者你覺得不舒服?僅僅因爲某些東西不適合你最喜歡的模型,並不意味着它是不好的或者錯誤的。這意味着它是不同的,並可能因合法原因而不同。您爲不同的任務使用不同的工具。

5

我最喜歡Haskell。最突出的Haskell網絡框架(相當於Rails和Django)被稱爲Yesod。它似乎有一個非常酷的,類型安全的多後端ORM。看看他們的書中的Persistance chapter

26

你應該看看紙「走出焦油坑」由本·莫斯利和彼得·馬克斯,請訪問:"Out of the Tar Pit" (Feb. 6, 2006)

這是一個現代的經典,詳細介紹所謂功能,關係編程一種編程範式/系統。雖然與數據庫沒有直接關係,但它討論瞭如何從系統的功能核心中隔離與外部世界的交互(例如數據庫)。

本文還討論瞭如何使用關係代數來定義和修改應用程序的內部狀態,這顯然與關係數據庫有關。

本文不會給出如何集成數據庫和函數式編程的確切答案,但它可以幫助您設計一個系統來最小化問題。

13

如果您的數據庫不會破壞信息,那麼您可以通過在整個數據庫的函數中作爲值工作,以符合「純功能性」編程值的功能方式使用它。

如果在時間T數據庫指出「Bob喜歡Suzie」,並且你有一個函數喜歡它接受一個數據庫和一個liker,那麼只要你可以在時間T恢復數據庫,你有一個純粹的功能程序涉及一個數據庫。例如

# Start: Time T 
likes(db, "Bob") 
=> "Suzie" 
# Change who bob likes 
... 
likes(db "Bob") 
=> "Alice" 
# Recover the database from T 
db = getDb(T) 
likes(db, "Bob") 
=> "Suzie" 

要做到這一點,你永遠不能扔掉,你可以使用(這在所有的實用性意味着你不能扔掉信息),因此您的存儲需求將單調遞增。但是,您可以開始使用數據庫作爲一系列離散值的線性數據,其中隨後的值通過事務與先前的值相關聯。例如,這是Datomic背後的主要思想。

20
  1. 功能語言沒有保持無狀態的目標,他們的目標是使狀態管理更明確。例如,在Haskell中,您可以將狀態monad視爲「正常」狀態的核心,並將IO monad視爲程序外部必須存在的狀態表示。這兩個monad都允許你(a)明確地表示有狀態的動作,(b)通過使用引用透明的工具來構建有狀態的動作。

  2. 您引用了許多ORM,根據它們的名稱,將抽象數據庫作爲對象集合。真的,這不是關係數據庫中的信息所代表的!根據它的名字,它代表關係數據。 SQL是用於處理關係數據集上的關係的代數(語言),實際上它本身非常「功能化」。 (a)ORM不是映射數據庫信息的唯一方法,(b)SQL對於某些數據庫設計來說實際上是一種非常好的語言,(c)函數式語言通常具有關係代數映射,它揭示了SQL在慣用語言(以及Haskell,typechecked)時尚方面的威力。

我會說最lisp是一個窮人的功能語言。它完全有能力根據現代功能實踐使用,但由於它不需要它們,所以社區不太可能使用它們。這導致了可能非常有用的方法混合,但肯定掩蓋了純函數接口仍然可以如何有效地使用數據庫。

2

數據庫是追蹤無狀態API中狀態的完美方式。如果您訂閱了REST,那麼您的目標是編寫與數據存儲(或其他後端)交互的無狀態代碼,以透明方式跟蹤狀態信息,以便您的客戶不必這樣做。

的對象關係映射器,在那裏你導入一個數據庫記錄作爲一個對象,然後修改它的想法,只是適用的和有用的函數式編程,因爲它是面向對象編程。需要注意的是,功能編程不會修改對象,但數據庫API可以允許您修改記錄。您的客戶端的控制流會是這個樣子:

  • 進口記錄作爲一個對象(數據庫API可以鎖定在這一點上的記錄),
  • 閱讀對象和分支基於其內容你喜歡,
  • 打包一個新的對象與您需要的修改,
  • 將新對象傳遞到適當的API調用更新數據庫上的記錄。

該數據庫將更新更改的記錄。純粹的函數式編程可能會禁止在程序的範圍內重新分配變量,但是您的數據庫API仍然可以允許就地更新。