Cơ sở dữ liệu và Lập trình chức năng có mâu thuẫn với nhau không?


122

Tôi đã là nhà phát triển web được một thời gian và gần đây đã bắt đầu học lập trình chức năng. Giống như những người khác, tôi đã gặp một số rắc rối đáng kể khi áp dụng nhiều khái niệm này vào công việc chuyên môn của mình. Đối với tôi, lý do chính cho điều này là tôi thấy mâu thuẫn giữa mục tiêu duy trì trạng thái không trạng thái của FP dường như khá mâu thuẫn với thực tế rằng hầu hết các công việc phát triển web mà tôi đã thực hiện đều gắn chặt với cơ sở dữ liệu, vốn rất tập trung vào dữ liệu.

Một điều khiến tôi trở thành nhà phát triển năng suất hơn nhiều ở khía cạnh OOP là việc phát hiện ra các trình ánh xạ quan hệ đối tượng như MyGeneration d00dads cho .Net, Class :: DBI cho perl, ActiveRecord cho ruby, v.v. Điều này cho phép tôi tránh xa từ việc viết các câu lệnh chèn và chọn cả ngày, và tập trung làm việc với dữ liệu dễ dàng dưới dạng các đối tượng. Tất nhiên, tôi vẫn có thể viết các truy vấn SQL khi sức mạnh của chúng là cần thiết, nhưng nếu không thì nó đã được trừu tượng hóa một cách độc đáo.

Bây giờ, chuyển sang lập trình chức năng, có vẻ như với nhiều khuôn khổ web FP như Liên kết yêu cầu viết rất nhiều mã sql soạn sẵn, như trong ví dụ này . Weblocks có vẻ tốt hơn một chút, nhưng nó dường như sử dụng loại mô hình OOP để làm việc với dữ liệu và vẫn yêu cầu mã được viết theo cách thủ công cho mỗi bảng trong cơ sở dữ liệu của bạn như trong ví dụ này . Tôi cho rằng bạn sử dụng một số cách tạo mã để viết các hàm ánh xạ này, nhưng điều đó có vẻ không giống với người nói ngọng.

(Lưu ý rằng tôi chưa xem xét các Weblocks hoặc các Liên kết một cách kỹ lưỡng, có thể tôi đang hiểu sai về cách chúng được sử dụng).

Vì vậy, câu hỏi đặt ra là, đối với các phần truy cập cơ sở dữ liệu (mà tôi tin là khá lớn) của ứng dụng web hoặc sự phát triển khác yêu cầu giao diện với cơ sở dữ liệu sql, chúng tôi dường như buộc phải xuống một trong các đường dẫn sau:

  1. Không sử dụng lập trình chức năng
  2. Truy cập Dữ liệu theo cách khó chịu, không trừu tượng liên quan đến việc viết thủ công nhiều mã SQL hoặc SQL giống như các Liên kết
  3. Buộc Ngôn ngữ chức năng của chúng tôi vào một mô hình giả OOP, do đó loại bỏ một số tính sang trọng và ổn định của lập trình chức năng thực sự.

Rõ ràng, không có lựa chọn nào trong số này có vẻ lý tưởng. Đã tìm ra một cách để tránh những vấn đề này? Có thực sự là một vấn đề ở đây?

Lưu ý: Cá nhân tôi quen thuộc nhất với LISP ở mặt trận FP, vì vậy nếu bạn muốn đưa ra bất kỳ ví dụ nào và biết nhiều ngôn ngữ FP, lisp có thể sẽ là ngôn ngữ được ưu tiên lựa chọn

Tái bút: Đối với Các vấn đề cụ thể cho các khía cạnh khác của phát triển web, hãy xem câu hỏi này .



4
Kiểm tra ClojureQL và HaskellDB. Chúng là các lớp trừu tượng sử dụng đại số quan hệ.
Masse

10
Bạn đang bắt đầu với tiền đề sai. Lập trình chức năng là tất cả về quản lý trạng thái rõ ràng và lành mạnh. Trên thực tế, chúng hoạt động rất tốt với cơ sở dữ liệu.
Lucian

3
SQL là một trong những ngôn ngữ tập trung vào lập trình chức năng thành công nhất, tôi không nghĩ rằng có bất kỳ khó khăn cố hữu nào.
Douglas

3
# 2 và # 3 của bạn là một sự phân đôi sai lầm. Viết SQL thô không phải là điều nhất thiết phải tránh và các phần trừu tượng trên cơ sở dữ liệu không nhất thiết phải là OOP-esque.
Dan Burton

Câu trả lời:


45

Trước hết, tôi sẽ không nói rằng CLOS (Hệ thống đối tượng Lisp chung) là "pseudo-OO". Nó là hạng nhất OO.

Thứ hai, tôi tin rằng bạn nên sử dụng mô hình phù hợp với nhu cầu của bạn.

Bạn không thể lưu trữ dữ liệu không trạng thái, trong khi một hàm là luồng dữ liệu và không thực sự cần trạng thái.

Nếu bạn có nhiều nhu cầu trộn lẫn, hãy kết hợp các mô hình của bạn. Đừng hạn chế bản thân chỉ sử dụng góc dưới bên phải của hộp công cụ.


3
chỉ cho vui thôi, tôi nghĩ tôi sẽ đề cập đến datalog cố gắng trở thành một cơ sở dữ liệu không trạng thái hơn. nó ghi lại tất cả các hành động, chẳng hạn như "người dùng thích bài đăng 1233." Những hành động này giải quyết trạng thái thực của cơ sở dữ liệu. Điều quan trọng là các truy vấn chỉ là sự thật chứ không phải là đột biến ...
Chet

80

Về vấn đề này từ góc độ của một người làm cơ sở dữ liệu, tôi thấy rằng các nhà phát triển giao diện người dùng đã cố gắng quá nhiều để tìm cách làm cho cơ sở dữ liệu phù hợp với mô hình của họ hơn là xem xét các cách hiệu quả nhất để sử dụng cơ sở dữ liệu không phải là hướng đối tượng hoặc chức năng mà là quan hệ và sử dụng lý thuyết tập hợp. Tôi đã thấy điều này thường dẫn đến mã hoạt động kém. Và hơn nữa nó tạo ra mã khó điều chỉnh hiệu suất.

Khi xem xét quyền truy cập cơ sở dữ liệu, có ba cân nhắc chính - tính toàn vẹn của dữ liệu (tại sao tất cả các quy tắc nghiệp vụ nên được thực thi ở cấp cơ sở dữ liệu không thông qua giao diện người dùng), hiệu suất và bảo mật. SQL được viết để quản lý hai cân nhắc đầu tiên hiệu quả hơn bất kỳ ngôn ngữ giao diện người dùng nào. Bởi vì nó được thiết kế đặc biệt để làm điều đó. Nhiệm vụ của cơ sở dữ liệu khác xa so với nhiệm vụ của giao diện người dùng. Có thắc mắc rằng loại mã hiệu quả nhất trong việc quản lý nhiệm vụ lại khác nhau về mặt khái niệm?

Và cơ sở dữ liệu lưu giữ thông tin quan trọng đối với sự tồn tại của một công ty. Có phải điều ngạc nhiên là các doanh nghiệp không sẵn sàng thử nghiệm các phương pháp mới khi sự sống còn của họ đang bị đe dọa. Rất nhiều doanh nghiệp không muốn nâng cấp lên các phiên bản mới của cơ sở dữ liệu hiện có của họ. Vì vậy, có sự bảo thủ cố hữu trong thiết kế cơ sở dữ liệu. Và nó cố tình như vậy.

Tôi sẽ không cố gắng viết T-SQL hoặc sử dụng các khái niệm thiết kế cơ sở dữ liệu để tạo giao diện người dùng của bạn, tại sao bạn lại cố gắng sử dụng ngôn ngữ giao diện và các khái niệm thiết kế để truy cập cơ sở dữ liệu của tôi? Bởi vì bạn nghĩ rằng SQL không đủ lạ mắt (hoặc mới)? Hoặc bạn không cảm thấy thoải mái với nó? Chỉ vì một cái gì đó không phù hợp với mẫu bạn cảm thấy thoải mái nhất, không có nghĩa là nó xấu hoặc sai. Nó có nghĩa là nó khác và có thể khác vì một lý do chính đáng. Bạn sử dụng một công cụ khác cho một nhiệm vụ khác.


"SQL được viết để quản lý hai cân nhắc đầu tiên hiệu quả hơn bất kỳ ngôn ngữ giao diện người dùng nào." Ồ, vậy à? Vậy tại sao các ràng buộc SQL vẫn không thể làm những việc như thế này ?
Robin Green

Nhưng một trình kích hoạt có thể, đó là một trong những mục đích chính của trình kích hoạt để có thể xử lý các ràng buộc phức tạp.
HLGEM

2
Tôi sẽ chuyển đoạn cuối cùng của bạn để nó là đoạn đầu. Điểm rất tốt, lặp lại những gì những người khác cũng đang thúc giục, đó là một cách tiếp cận đa mô hình (không phải một kích thước phù hợp với tất cả).
pestophagous

31

Bạn nên xem bài báo "Out of the Tar Pit" của Ben Moseley và Peter Marks, có sẵn tại đây: "Out of the Tar Pit" (ngày 6 tháng 2 năm 2006)

Nó là một tác phẩm cổ điển hiện đại trình bày chi tiết một mô hình / hệ thống lập trình được gọi là Lập trình quan hệ chức năng. Mặc dù không liên quan trực tiếp đến cơ sở dữ liệu, nhưng nó thảo luận về cách tách biệt các tương tác với thế giới bên ngoài (ví dụ: cơ sở dữ liệu) khỏi lõi chức năng của hệ thống.

Bài báo cũng thảo luận về cách triển khai một hệ thống trong đó trạng thái bên trong của ứng dụng được xác định và sửa đổi bằng cách sử dụng đại số quan hệ, rõ ràng là liên quan đến cơ sở dữ liệu quan hệ.

Bài báo này sẽ không đưa ra câu trả lời chính xác về cách tích hợp cơ sở dữ liệu và lập trình chức năng, nhưng nó sẽ giúp bạn thiết kế một hệ thống để giảm thiểu vấn đề.


4
Thật là một bài báo tuyệt vời. Các liên kết mà bạn cung cấp cho bị phá vỡ (tạm thời?), Nhưng tôi thấy bài báo cũng tại shaffner.us/cs/papers/tarpit.pdf
pestophagous

2
@queque Liên kết gốc vẫn chưa chết. Tôi đặt liên kết mới vào câu trả lời của Kevin.
David Tonhofer

25
  1. Các ngôn ngữ chức năng không có mục tiêu duy trì trạng thái không trạng thái, chúng có mục tiêu làm cho việc quản lý trạng thái trở nên rõ ràng. Ví dụ, trong Haskell, bạn có thể coi đơn nguyên trạng thái là trung tâm của trạng thái "bình thường" và đơn nguyên IO là đại diện của trạng thái phải tồn tại bên ngoài chương trình. Cả hai đơn nguyên này đều cho phép bạn (a) thể hiện rõ ràng các hành động có trạng thái và (b) xây dựng các hành động có trạng thái bằng cách soạn chúng bằng các công cụ minh bạch có tham chiếu.

  2. Bạn tham chiếu một số ORM, theo tên của chúng, cơ sở dữ liệu trừu tượng dưới dạng tập hợp các đối tượng. Thực sự, đây không phải là thông tin trong cơ sở dữ liệu quan hệ đại diện! Theo tên của nó, nó đại diện cho dữ liệu quan hệ. SQL là một đại số (ngôn ngữ) để xử lý các mối quan hệ trên một tập dữ liệu quan hệ và bản thân nó thực sự khá "chức năng". Tôi đưa ra điều này để xem xét rằng (a) ORM không phải là cách duy nhất để ánh xạ thông tin cơ sở dữ liệu, (b) SQL thực sự là một ngôn ngữ khá đẹp cho một số thiết kế cơ sở dữ liệu và (c) rằng các ngôn ngữ hàm thường có đại số quan hệ ánh xạ thể hiện sức mạnh của SQL theo kiểu thành ngữ (và trong trường hợp của Haskell, được đánh máy).

Tôi có thể nói rằng hầu hết những người nói ngọng là ngôn ngữ chức năng của một người nghèo. Nó hoàn toàn có khả năng được sử dụng theo các hoạt động chức năng hiện đại, nhưng vì nó không yêu cầu chúng nên cộng đồng ít có khả năng sử dụng chúng hơn. Điều này dẫn đến một hỗn hợp các phương pháp có thể rất hữu ích nhưng chắc chắn che khuất cách các giao diện chức năng thuần túy vẫn có thể sử dụng cơ sở dữ liệu một cách có ý nghĩa.


15

Tôi không nghĩ rằng bản chất không trạng thái của các ngôn ngữ fp là một vấn đề với việc kết nối với cơ sở dữ liệu. Lisp là một ngôn ngữ lập trình chức năng không thuần túy vì vậy nó sẽ không gặp bất kỳ vấn đề gì khi xử lý trạng thái. Và các ngôn ngữ lập trình chức năng thuần túy như Haskell có những cách xử lý đầu vào và đầu ra có thể áp dụng cho việc sử dụng cơ sở dữ liệu.

Từ câu hỏi của bạn, có vẻ như vấn đề chính của bạn nằm ở việc tìm ra cách tốt để trừu tượng hóa dữ liệu dựa trên bản ghi mà bạn lấy lại từ cơ sở dữ liệu của mình thành một thứ gì đó là lisp-y (lisp-ish?) Mà không cần phải viết nhiều SQL mã. Điều này có vẻ giống như một vấn đề với công cụ / thư viện hơn là một vấn đề với mô hình ngôn ngữ. Nếu bạn muốn thực hiện FP thuần túy thì có thể nói ngọng không phải là ngôn ngữ phù hợp với bạn. Nói ngọng thông thường dường như tích hợp nhiều ý tưởng hay từ oo, fp và các mô hình khác hơn là về fp thuần túy. Có lẽ bạn nên sử dụng Erlang hoặc Haskell nếu bạn muốn đi theo con đường FP thuần túy.

Tôi nghĩ rằng những ý tưởng 'giả oo' trong ngọng cũng có công của chúng. Bạn có thể muốn thử chúng. Nếu chúng không phù hợp với cách bạn muốn làm việc với dữ liệu của mình, bạn có thể thử tạo một lớp bên trên các Weblocks cho phép bạn làm việc với dữ liệu của mình theo cách bạn muốn. Điều này có thể dễ dàng hơn so với việc tự viết mọi thứ.

Tuyên bố từ chối trách nhiệm: Tôi không phải là chuyên gia Lisp. Tôi chủ yếu quan tâm đến các ngôn ngữ lập trình và đã chơi với Lisp / CLOS, Scheme, Erlang, Python và một chút Ruby. Trong cuộc sống lập trình hàng ngày, tôi vẫn buộc phải sử dụng C #.


3
Erlang không phải là FP thuần túy theo bất kỳ định nghĩa nào của từ này. Bạn viết erlang bằng cách tạo ra rất nhiều quy trình (tất cả đều chạy song song) gửi thông điệp đến các đối tượng khác nhau, giống như các đối tượng trong smalltalk. Vì vậy, từ góc độ cấp cao, nó thậm chí có vẻ hơi OO-ish, và chắc chắn có trạng thái. Nếu bạn phóng to (vào đoạn mã đang chạy bên trong một quy trình), nó trông có vẻ hoạt động hơn, nhưng vẫn không hoàn toàn hoạt động, vì bạn có thể gửi tin nhắn (và do đó làm I / O, v.v.) từ bất kỳ nơi nào bạn muốn, cũng như lưu trữ " biến toàn cục "(toàn cầu cho quá trình, bên trong một thứ gọi là" process dict ".)
Amadiro

@Amadiro "chắc chắn có bang". Tất nhiên là thế. Chúng tôi luôn có trạng thái. Vấn đề không phải là trạng thái, nó là trạng thái có thể thay đổi . Một phần tốt của "lập trình chức năng" là loại bỏ các biểu diễn trạng thái dễ vỡ (ví dụ: các cá thể đối tượng bị thay đổi bởi các quy trình khác trong khi chúng tôi giữ tham chiếu đến chúng, theo cách không giao dịch để thêm xúc phạm đến thương tích). Erlang có trạng thái không thể thay đổi và do đó đáp ứng tiêu chí này của lập trình chức năng. Và do đó: Cơ sở dữ liệu thuộc bất kỳ loại nào không bao giờ là vấn đề. Cập nhật cơ sở dữ liệu là một vấn đề (xem thêm khẳng định khó chịu trong Prolog).
David Tonhofer

15

Nếu cơ sở dữ liệu của bạn không phá hủy thông tin, thì bạn có thể làm việc với nó theo cách chức năng nhất quán với các giá trị lập trình "chức năng thuần túy" bằng cách làm việc trong các chức năng của toàn bộ cơ sở dữ liệu dưới dạng một giá trị.

Nếu tại thời điểm T cơ sở dữ liệu nói rằng "Bob thích Suzie" và bạn có một hàm thích chấp nhận một cơ sở dữ liệu và một liker, thì miễn là bạn có thể khôi phục cơ sở dữ liệu tại thời điểm T, bạn có một chương trình chức năng thuần túy liên quan đến cơ sở dữ liệu . ví dụ

# 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"

Để làm được điều này, bạn không thể vứt bỏ thông tin mà bạn có thể sử dụng (trong thực tế có nghĩa là bạn không thể vứt bỏ thông tin), vì vậy nhu cầu lưu trữ của bạn sẽ tăng lên một cách đơn điệu. Nhưng bạn có thể bắt đầu làm việc với cơ sở dữ liệu của mình dưới dạng một chuỗi giá trị rời rạc tuyến tính, trong đó các giá trị tiếp theo có liên quan đến các giá trị trước đó thông qua các giao dịch.

Ví dụ, đây là ý tưởng chính đằng sau Datomic .


Đẹp. Tôi thậm chí còn không biết về Datomic. Xem thêm: cơ sở lý luận về Datomic .
David Tonhofer

12

Không có gì. Có một loại cơ sở dữ liệu được gọi là 'Cơ sở dữ liệu chức năng', trong đó Mnesia có lẽ là ví dụ dễ tiếp cận nhất. Nguyên tắc cơ bản là lập trình chức năng là khai báo, vì vậy nó có thể được tối ưu hóa. Bạn có thể triển khai một phép nối bằng cách sử dụng Tính năng toàn diện danh sách trên các tập hợp liên tục và trình tối ưu hóa truy vấn có thể tự động tìm ra cách triển khai quyền truy cập đĩa.

Mnesia được viết bằng Erlang và có ít nhất một khuôn khổ web ( Erlyweb ) có sẵn cho nền tảng đó. Erlang vốn tồn tại song song với mô hình phân luồng không chia sẻ gì, vì vậy theo một số cách nhất định, nó tự cho mình là kiến ​​trúc có thể mở rộng.


1
Tôi không nghĩ đó là một giải pháp. Cũng có cơ sở dữ liệu hướng đối tượng, nhưng thông thường, bạn muốn kết nối với cơ sở dữ liệu SQL quan hệ cũ thuần túy.
jalf

4
Bạn vẫn có trở kháng không khớp với các ngôn ngữ và cơ sở dữ liệu OO giống như cách bạn có trở kháng chức năng-SQL không khớp.
ConcernedOfTunbridgeWells

1
@ConcernedOfTunbridgeWells Tôi sẽ tùy tiện tuyên bố rằng sự không phù hợp trở kháng này là một phần trong trí tưởng tượng của những người có búa, những người cần mọi thứ đều là đinh. Các lớp rất mỏng và kiến ​​thức về SQL có thể đi một chặng đường dài, do đó jOOQ và các miếng chêm tương tự.
David Tonhofer

6

Cơ sở dữ liệu là cách hoàn hảo để theo dõi trạng thái trong một API không trạng thái. Nếu bạn đăng ký REST, thì mục tiêu của bạn là viết mã không trạng thái tương tác với kho dữ liệu (hoặc một số chương trình phụ trợ khác) theo dõi thông tin trạng thái một cách minh bạch để khách hàng của bạn không phải làm vậy.

Ý tưởng về Object-Relational Mapper, nơi bạn nhập một bản ghi cơ sở dữ liệu dưới dạng một đối tượng và sau đó sửa đổi nó, cũng có thể áp dụng và hữu ích cho lập trình chức năng như đối với lập trình hướng đối tượng. Một lưu ý là lập trình chức năng không sửa đổi đối tượng tại chỗ, nhưng API cơ sở dữ liệu có thể cho phép bạn sửa đổi bản ghi tại chỗ. Luồng kiểm soát của khách hàng của bạn sẽ trông giống như sau:

  • Nhập bản ghi dưới dạng một đối tượng (API cơ sở dữ liệu có thể khóa bản ghi tại thời điểm này),
  • Đọc đối tượng và phân nhánh dựa trên nội dung của nó như bạn muốn,
  • Đóng gói một đối tượng mới với các sửa đổi mong muốn của bạn,
  • Chuyển đối tượng mới đến lệnh gọi API thích hợp cập nhật bản ghi trên cơ sở dữ liệu.

Cơ sở dữ liệu sẽ cập nhật bản ghi với các thay đổi của bạn. Lập trình chức năng thuần túy có thể không cho phép gán lại các biến trong phạm vi chương trình của bạn , nhưng API cơ sở dữ liệu của bạn vẫn có thể cho phép cập nhật tại chỗ.


5

Tôi thấy thoải mái nhất với Haskell. Khung công tác web Haskell nổi bật nhất (có thể so sánh với Rails và Django) được gọi là Yesod. Có vẻ như nó có một ORM đa phụ trợ khá thú vị, an toàn. Hãy xem chương Kiên trì trong cuốn sách của họ.


0

Cơ sở dữ liệu và Lập trình chức năng có thể được hợp nhất.

ví dụ:

Clojure là một ngôn ngữ lập trình chức năng dựa trên lý thuyết cơ sở dữ liệu quan hệ.

               Clojure -> DBMS, Super Foxpro
                   STM -> TransactionMVCC
Persistent Collections -> db, table, col
              hash-map -> indexed data
                 Watch -> trigger, log
                  Spec -> constraint
              Core API -> SQL, Built-in function
              function -> Stored Procedure
             Meta Data -> System Table

Lưu ý: Trong spec2 mới nhất, spec giống RMDB hơn. xem: spec-alpha2 wiki: Schema-and-select

Tôi ủng hộ: Xây dựng mô hình dữ liệu quan hệ trên bản đồ băm để đạt được sự kết hợp giữa các lợi thế của NoSQL và RMDB. Đây thực sự là một triển khai ngược lại của posgtresql.

Duck Typing: Nếu nó giống một con vịt và cút giống một con vịt, nó phải là một con vịt.

Nếu mô hình dữ liệu của clojure như RMDB, các cơ sở của clojure như RMDB và thao tác dữ liệu của clojure như RMDB, thì clojure phải là RMDB.

Clojure là một ngôn ngữ lập trình chức năng dựa trên lý thuyết cơ sở dữ liệu quan hệ

Mọi thứ đều là RMDB

Triển khai mô hình dữ liệu quan hệ và lập trình dựa trên bản đồ băm (NoSQL)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.