Trong những lĩnh vực nào việc sử dụng F # có thể phù hợp hơn C #? [đóng cửa]


210

Trong vài năm qua, F # đã phát triển thành một trong những ngôn ngữ được hỗ trợ đầy đủ của Microsoft sử dụng nhiều ý tưởng được đưa vào OCaml, ML và Haskell.

Trong vài năm qua, C # đã mở rộng các tính năng mục đích chung của mình bằng cách giới thiệu ngày càng nhiều tính năng ngôn ngữ chức năng: LINQ (hiểu danh sách), Lambdas, Closures, Đại biểu ẩn danh và hơn thế nữa ...

Với việc áp dụng các tính năng chức năng này của C # và phân loại của F # như một ngôn ngữ chức năng không tinh khiết (nó cho phép BẠN truy cập các thư viện khung hoặc thay đổi trạng thái chia sẻ khi một hàm được gọi nếu bạn muốn) có sự tương đồng mạnh mẽ giữa hai ngôn ngữ riêng cực đối lập nhấn mạnh chính.

Tôi quan tâm đến bất kỳ mô hình thành công nào sử dụng hai ngôn ngữ này trong các chương trình polyglot sản xuất của bạn và cả các lĩnh vực trong phần mềm sản xuất (ứng dụng web, ứng dụng khách, ứng dụng máy chủ) mà bạn đã viết trong F # trong năm qua hoặc trước đó bạn sẽ có được viết bằng C #.

Câu trả lời:


258

Tôi đã viết một ứng dụng để cân bằng lịch trình sản xuất điện quốc gia cho một danh mục các nhà máy điện đến một vị trí giao dịch cho một công ty năng lượng. Các thành phần máy khách và máy chủ ở trong C # nhưng công cụ tính toán được viết bằng F #.

Việc sử dụng F # để giải quyết sự phức tạp ở trung tâm của ứng dụng này thể hiện rõ ràng một điểm ngọt ngào cho ngôn ngữ trong phần mềm doanh nghiệp, cụ thể là phân tích phức tạp về mặt thuật toán của các tập dữ liệu lớn. Kinh nghiệm của tôi là một điều rất tích cực. Đặc biệt:

Đơn vị đo Công nghiệp tôi làm việc nằm rải rác với các đơn vị. Các phương trình tôi thực hiện (thường có tính chất hình học) xử lý các đơn vị thời gian, sức mạnh và năng lượng. Có hệ thống loại xác minh tính chính xác của các đơn vị đầu vào và đầu ra của các chức năng là một trình tiết kiệm thời gian rất lớn, cả về kiểm tra và đọc / hiểu mã. Nó xóa bỏ cả một lớp lỗi mà các hệ thống trước đây dễ mắc phải.

Lập trình khám phá Làm việc với các tệp tập lệnh và REPL (F # Interactive) cho phép tôi khám phá không gian giải pháp hiệu quả hơn trước khi cam kết thực hiện so với vòng lặp chỉnh sửa / biên dịch / chạy / kiểm tra truyền thống. Đó là một cách rất tự nhiên để một lập trình viên xây dựng sự hiểu biết của họ về vấn đề và những căng thẳng trong thiết kế khi chơi.

Kiểm thử đơn vị Mã được viết bằng các hàm hiệu ứng không phụ và cấu trúc dữ liệu bất biến là một niềm vui để kiểm tra. Không có tương tác phụ thuộc thời gian phức tạp để làm hỏng mọi thứ hoặc tập hợp lớn các phụ thuộc sẽ bị chế giễu.

Tương tác Tôi đã xác định giao diện cho công cụ tính toán trong C # và thực hiện tính toán trong F #. Công cụ tính toán sau đó có thể được đưa vào bất kỳ mô-đun C # nào cần sử dụng nó mà không có bất kỳ mối lo ngại nào về khả năng tương tác. Liền mạch. Các lập trình viên C # không bao giờ cần biết.

Giảm mã Phần lớn dữ liệu được đưa vào công cụ tính toán ở dạng vectơ và ma trận. Các hàm bậc cao hơn ăn những thứ này cho bữa sáng với sự ồn ào tối thiểu, mã tối thiểu. Xinh đẹp.

Thiếu lỗi Lập trình chức năng có thể cảm thấy kỳ lạ. Tôi có thể đang làm việc trên một thuật toán, cố gắng hết sức để có được mã để vượt qua trình kiểm tra loại nhưng một khi trình kiểm tra loại được thỏa mãn thì nó hoạt động. Nó gần như nhị phân, hoặc nó sẽ không biên dịch hoặc chính xác. Lỗi trường hợp kỳ lạ được giảm thiểu, đệ quy và các hàm bậc cao hơn loại bỏ rất nhiều mã giữ sách giới thiệu lỗi trường hợp cạnh.

Tính song song Độ tinh khiết chức năng của việc thực hiện kết quả làm cho nó chín muồi để khai thác tính song song vốn có trong xử lý các vectơ dữ liệu. Có lẽ đây là nơi tôi sẽ đi tiếp theo khi .NET 4 ra mắt.


18
+1 để giải thích lý do tại sao F # rất phù hợp với động cơ crunching số. Một (ảo) +1 khác để đề cập đến các đơn vị đo lường. Đó là một phần của ngôn ngữ xứng đáng được đề cập thường xuyên hơn.
cfern

5
Câu trả lời tuyệt vời, phù hợp, hiện đại và phác thảo sự phù hợp của F # để đối phó với sự phức tạp, tôi đã học được rất nhiều từ việc đọc nó, cảm ơn
Peter McG

Simon trả lời tuyệt vời, và như Don đã đề cập tối qua, được trích dẫn trong các slide gần đây của anh ấy. Thời gian để thêm một liên kết "thêm vào giỏ hàng"?
Chris Ballard

1
xin chào, bạn có được phép cho chúng tôi biết thêm về kiến ​​trúc ứng dụng của bạn không?
Nikos

76

Trong thời gian thực tập tại Microsoft Research, tôi đã làm việc trên một số phần của Visual Studio IntelliSense cho F # (được viết bằng F #). Tôi đã có một số kinh nghiệm với IntelliSense từ các dự án C # trước đó, vì vậy tôi nghĩ rằng tôi có thể so sánh hai dự án.

  • Khả năng mở rộng của Visual Studio vẫn dựa trên COM, vì vậy bạn cần xử lý các đối tượng không phải là đối tượng .NET rất đẹp (và chắc chắn không hoạt động), nhưng tôi không cảm thấy có sự khác biệt lớn nào giữa C # và F # (nó hoạt động trơn tru từ F#)

  • Các cấu trúc dữ liệu được sử dụng để thể hiện mã chương trình trong F # hầu hết là các hiệp hội bị phân biệt đối xử (không được hỗ trợ trong C # theo bất kỳ cách hợp lý nào) và điều này tạo ra sự khác biệt lớn cho loại ứng dụng này (nơi bạn cần xử lý các cấu trúc cây, như mã chương trình ). Các hiệp hội phân biệt và khớp mẫu cho phép bạn cấu trúc mã tốt hơn (giữ chức năng liên quan ở một nơi thay vì đặt nó ở khắp mọi nơi trong các phương thức ảo)

Trước đó, tôi cũng đã làm việc với nhà cung cấp CodeDOM cho F # (cũng được viết bằng F #). Tôi thực sự đã làm thí nghiệm đầu tiên trong C #, nhưng sau đó đã chuyển đổi mã thành F #.

  • Nhà cung cấp CodeDOM cần duyệt qua một số cấu trúc được biểu diễn bằng các đối tượng .NET, vì vậy không có nhiều không gian để phát minh ra các biểu diễn dữ liệu của riêng bạn (đó là lĩnh vực mà F # có thể mang lại lợi ích tốt).

  • Tuy nhiên, có nhiều tính năng F # nhỏ giúp thực hiện công việc dễ dàng hơn. Vì bạn cần tạo một chuỗi, tôi đã xác định các toán tử tùy chỉnh để xây dựng chuỗi (sử dụng StringBuilder) và triển khai mã bằng cách sử dụng chúng và các hàm bậc cao hơn (ví dụ: định dạng danh sách các đối tượng được phân tách bằng chuỗi đã chỉ định, v.v.), đã loại bỏ rất nhiều sự lặp lại (và foreachcác vòng lặp tẻ nhạt ).

Đây là hai ví dụ tương đối cụ thể, nhưng cả hai đều liên quan đến việc làm việc với các biểu diễn của chương trình hoặc biểu thức hoặc nói chung hơn là các cấu trúc dữ liệu giống như cây phức tạp. Tôi nghĩ rằng trong lĩnh vực này, F # chắc chắn là một lựa chọn tốt (bất kể các tính năng chức năng trong C #).


6
Rất thú vị, nhiều bằng chứng cho thấy sự gia tăng của F # trong Microsoft chắc chắn là rất cao, thật là một kỳ thực tập tuyệt vời phải có!
Peter McG

43

Chúng tôi đã chuyển sản phẩm thương mại đầu tiên trên thế giới được viết bằng F # ( F # cho Visualization ) và thứ hai ( F # cho Numerics ) cũng như tài liệu thương mại đầu tiên trên F # ( Tạp chí F # .NET ) và viết và xuất bản cuốn sách duy nhất về phiên bản hiện tại của F # ( Visual F # 2010 cho tính toán kỹ thuật ).

Chúng tôi đã vận chuyển các sản phẩm dọc theo các dòng tương tự được viết bằng C # (ví dụ: cái này ) nhưng chúng tôi cũng có một nền tảng vững chắc trong việc sử dụng thương mại của OCaml. Chúng tôi đã nhiệt tình chấp nhận F # khi nó vẫn còn là một nguyên mẫu nghiên cứu vào năm 2006 bởi vì chúng tôi nhận ra tiềm năng của việc có một ngôn ngữ giống như OCaml hiện đại trên nền tảng .NET sức mạnh công nghiệp và do đó, chúng tôi đã thúc đẩy nó được sản xuất. Kết quả là một thành công đáng kinh ngạc và F # đã vượt xa sự mong đợi cao cả của chúng tôi.

Đối với chúng tôi, F # có nhiều lợi thế khác nhau và chúng tôi sử dụng nó cho nhiều ứng dụng. Chúng tôi có hàng trăm ngàn dòng mã F # đang được sản xuất. Bây giờ chúng tôi sử dụng F # cho tất cả các ứng dụng LOB của mình: giao dịch thẻ tín dụng của chúng tôi được xử lý bằng mã F #, thông báo sản phẩm của chúng tôi được gửi bằng mã F #, đăng ký của chúng tôi được xử lý bằng mã F #, tài khoản của chúng tôi được thực hiện bằng mã F #, v.v. Có lẽ tính năng ngôn ngữ chính trả cổ tức ở đây là khớp mẫu. Chúng tôi thậm chí đã sử dụng F # để tô màu cú pháp làm nổi bật cuốn sách mới nhất của chúng tôi ...

Thư viện trực quan của chúng tôi là một người bán lớn và các trung tâm chức năng của nó trên F # tương tác đang chạy trong Visual Studio. Thư viện của chúng tôi tăng cường điều này với khả năng tạo ra các hình ảnh 2D và 3D tương tác với nỗ lực tối thiểu (ví dụ: chỉPlot([Function sin], (-6., 6.))để vẽ một sóng hình sin). Đặc biệt, tất cả các vấn đề luồng được hoàn toàn tự động để người dùng không phải lo lắng về các luồng và giao diện người dùng. Các hàm hạng nhất và sự lười biếng là vô cùng quý giá khi viết phần này của thư viện và các kiểu dữ liệu đại số được sử dụng rộng rãi ở những nơi khác. Hiệu suất có thể dự đoán cũng được chứng minh là có giá trị ở đây khi khách hàng của chúng tôi gặp phải lỗi hiệu suất trong thử nghiệm lần truy cập của WPF và có thể dễ dàng thực hiện lại mã có liên quan trong F # để cải thiện hiệu suất 10.000 ×. Do tính chất dạng tự do của GUI của sản phẩm này, trình thiết kế GUI và C # sẽ không có lợi.

Phần lớn công việc của chúng tôi xoay quanh các phương pháp số, bao gồm cả thư viện thương mại và sách của chúng tôi. F # mạnh hơn nhiều trong lĩnh vực này so với C # vì nó cung cấp các bản tóm tắt cấp cao (ví dụ: các hàm bậc cao hơn) với các hình phạt hiệu suất tối thiểu. Kết quả hấp dẫn nhất của chúng tôi trong bối cảnh này là việc tạo ra một triển khai phân tách QR đơn giản nhưng tổng quát từ đại số tuyến tính ngắn hơn 20 lần so với mã Fortran từ việc triển khai LAPACK tham chiếu, nhanh hơn 3 lần so với Intel Math được điều chỉnh bởi nhà cung cấp Thư viện hạt nhân và chung chung hơn vì mã của chúng tôi có thể xử lý các ma trận thuộc bất kỳ loại nào, thậm chí là ma trận tượng trưng!

Chúng tôi hiện đang phát triển các thành phần WPF / Silverlight trong hỗn hợp F # (cho ruột) và C # (cho shim), xây dựng các ứng dụng WPF để làm hướng dẫn tương tác cho các sản phẩm phần mềm của chúng tôi và tôi đang viết một cuốn sách mới, Multicore F #, sẽ là hướng dẫn dứt khoát cho lập trình song song bộ nhớ chia sẻ trên .NET.


Bạn có giống Jon Harrop đã viết "F # cho các nhà khoa học" không?
Andre Artus

7
Đúng. Tôi đã viết F # cho các nhà khoa học 5 năm trước.
Jon Harrop

Bạn có tham khảo một số loại cho mã phân tách QR trong F # mà bạn đề cập trong đoạn áp chót của mình không? Cảm ơn.
Samik R

@SamikR: Không, xin lỗi. Đó là mã thương mại. Thật dễ dàng để viết mặc dù.
Jon Harrop

@Jon có từ nào trên Multicore F # không?
giáo sư bigglesworth

25

Trong hơn 6 tháng qua, tôi đã làm việc trên lớp mô phỏng Vim cho Visual Studio 2010. Đây là một sản phẩm miễn phí với tất cả các nguồn có sẵn miễn phí trên github

Dự án được chia thành 3 DLL đại diện cho một lớp riêng biệt. Mỗi lớp có một dll thử nghiệm đơn vị tương ứng.

  1. Động cơ Vim: F #
  2. Lớp WPF để trang trí và tích hợp trình soạn thảo: C #
  3. Lớp tích hợp Visual Studio: C #

Đây là dự án lớn đầu tiên tôi từng thực hiện với F # và tôi phải nói rằng tôi yêu ngôn ngữ này. Theo nhiều cách, tôi đã sử dụng dự án này như một phương pháp học F # (và đường cong học tập này rất rõ ràng nếu bạn xem qua lịch sử của dự án).

Điều tôi thấy tuyệt vời nhất về F # là sự ngắn gọn của ngôn ngữ. Công cụ Vim bao gồm phần lớn logic nhưng nó chỉ bao gồm 30% cơ sở mã tổng thể.


19
Trình chỉnh sửa ... ngôn ngữ chức năng ... mô phỏng vi ... bạn đã phát minh lại emacs. NOOOOOOOOOOOOOOOOOOOOOOO!
Ben Voigt

2
Ngoại trừ việc đó là "Được chứng nhận 100% không có dấu ngoặc đơn" :)
Pavel Minaev

@Pavel, ngoại trừ các bộ dữ liệu tất nhiên và các cuộc gọi phương thức .net
JaredPar

26
Hai điều cần lưu ý ở đây. Trước hết, các tuple không cần ()trong F # - ,toán tử là thứ tạo ra chúng, vì vậy, let x = 1,2một tuple hợp lệ đã không có bất kỳ dấu ngoặc nào. Thứ hai, bất kỳ cặp parens nào trong F # đều có thể được thay thế bằng các cặp begin.. end(điều này được kế thừa từ ML) - vì vậy, ví dụ, "foo".IndexOf begin 'a', 1 endlà một cuộc gọi phương thức .NET hợp lệ. Vì vậy, nếu bạn từng muốn được miễn phí, F # là một ngôn ngữ cho phép bạn làm điều đó :)
Pavel Minaev

Bình luận hài hước Pavel! Không biết điều đó. Tôi nghĩ rằng trong một số trường hợp với các khối nhóm lớn, tôi thực sự có thể thích begin.. end. CSONG: QUY LUẬT VsVim!
Dan Fitch

13

Rất nhiều bài kiểm tra đơn vị cho các thành phần F # Visual Studio được viết bằng F #. Họ chạy bên ngoài VS, chế nhạo các bit Visual Studio khác nhau. Khả năng hỗ trợ các đối tượng ẩn danh thực hiện các giao diện là hữu ích thay cho khung / công cụ chế nhạo. Tôi chỉ có thể viết

let owpe : string list ref = ref []
let vsOutputWindowPane = 
    { new IVsOutputWindowPane with
        member this.Activate () = err(__LINE__)
        member this.Clear () = owpe := []; 0
        member this.FlushToTaskList () = VSConstants.S_OK
        member this.GetName(pbstrPaneName) = err(__LINE__)
        member this.Hide () = err(__LINE__)
        member this.OutputString(pszOutputString) = owpe := pszOutputString :: !owpe ; 0
        member this.OutputStringThreadSafe(pszOutputString) = owpe := pszOutputString :: !owpe ; 0
        member this.OutputTaskItemString(pszOutputString, nPriority, nCategory, pszSubcategory, nBitmap, pszFilename, nLineNum, pszTaskItemText) = err(__LINE__)
        member this.OutputTaskItemStringEx(pszOutputString, nPriority, nCategory, pszSubcategory, nBitmap, pszFilename, nLineNum, pszTaskItemText, pszLookupKwd) = err(__LINE__)
        member this.SetName(pszPaneName) = err(__LINE__)
    }            
DoSomethingThatNeedsA(vsOutputWindowPane)
assert( !owpe = expectedOutputStringList )

khi tôi cần một thể hiện của ví dụ một IVsOutputWindowPaneđể vượt qua đối với một số thành phần khác mà cuối cùng sẽ được gọi OutputStringClear, và sau đó kiểm tra string list refđối tượng ở phần cuối của bài kiểm tra để xem nếu sản lượng dự kiến được viết ra.


Thật thú vị, nhiều bằng chứng cho thấy sự gia tăng của F # trong Microsoft chắc chắn là cao. Tôi không biết bạn có thể tạo các đối tượng ẩn danh thực hiện giao diện trong F #
Peter McG

9

Chúng tôi đã viết một ngôn ngữ công cụ quy tắc tùy chỉnh bằng cách sử dụng triển khai Lex-Yacc trong F #

EDIT để bao gồm trả lời bình luận

Không có triển khai lex / yacc trong C #. (theo như chúng tôi đã biết, và F # one là)

Nó đã có thể, nhưng một nỗi đau hết sức để xây dựng phân tích cú pháp.

Chủ đề này cho thấy một số đề xuất khác, chẳng hạn như các thư viện bên ngoài, nhưng kiến ​​trúc sư trưởng của chúng tôi là một tay cũ trong các ngôn ngữ chức năng, vì vậy lựa chọn sử dụng F # là không có trí tuệ.


+1 Trước đây bạn có thể viết điều này bằng C # là không phù hợp hoặc chậm hơn vì một lý do nhất định?
Peter McG

@Peter McGrattan Ít nhất tại thời điểm viết (phần mềm), không có triển khai lex / yacc trong C #. Nó đã có thể, nhưng một nỗi đau hết sức để xây dựng phân tích cú pháp. stackoverflow.com/questions/540593/lex-yacc-for-c hiển thị một số đề xuất khác, nhưng kiến ​​trúc sư chính của chúng tôi là một tay cũ trong các ngôn ngữ chức năng, vì vậy lựa chọn sử dụng F # là không có trí tuệ
johnc

nếu bạn nghĩ rằng không có lex / yacc cho C # thì tôi sợ rằng bạn chỉ đơn giản là không đủ cứng cho nó (Có một cái cũ hơn F #) nói rằng nếu bạn cần lex / yacc F # thì theo tôi thì phù hợp hơn nhiều búa cho móng đó hơn c #
Rune FS

Bản thân tôi đã sử dụng F # với fslex / fxyacc, mặc dù không phải trong dự án "sản xuất" (dù sao vẫn chưa được phát hành) - Làm nổi bật cú pháp MSIL và mở rộng hoàn thành mã cho VS. Lợi ích chính của việc sử dụng F # là bạn có được các ADT, rất thuận tiện để đại diện cho các cây phân tích cú pháp. Ngoài ra, sử dụng khóa kéo ( en.wikipedia.org/wiki/Zipper_(data_structure) ) giúp bạn dễ dàng thực hiện từ vựng gia tăng - và khóa kéo, có chức năng, dễ dàng thao tác chính xác hơn trong F #.
Pavel Minaev

7

Tôi hiện đang làm việc trên một trình biên dịch cho một ngôn ngữ lập trình. Trình biên dịch được viết hoàn toàn bằng F #. Trình biên dịch (ngoài việc xây dựng lex và trình phân tích cú pháp với lex / yacc) về cơ bản được xây dựng dưới dạng nhiều biến đổi của một cấu trúc giống như cây phức tạp.

Như đã lưu ý bởi những người khác phân biệt các hiệp hội và khớp mẫu làm cho việc làm việc với loại cấu trúc dữ liệu này dễ dàng hơn nhiều so với việc bỏ mã trong các phương thức ảo "ở mọi nơi"

Tôi đã không thực hiện bất kỳ công việc F # nào trước khi tôi bắt đầu làm việc với trình biên dịch (tuy nhiên tôi đã có các trình biên dịch buld trong một biến thể OCaml khác gọi là MoscowML) và giống như Jared nói rằng nó có thể nhìn thấy từ đoạn mã mà tôi đã làm trước tiên nhưng nói chung tôi thấy F # dễ dàng để học cách nhập tâm trí FP một lần nữa sau khi mã hóa chủ yếu là OO trong một thập kỷ sẽ mất nhiều thời gian hơn.

làm việc với cây sang một bên Tôi thấy khả năng viết mã khai báo lợi ích chính của FP (bao gồm F #) có mã mô tả thuật toán Tôi đang cố gắng thực hiện trái ngược với C # mô tả cách tôi triển khai thuật toán đại số là một lợi thế rất lớn.


6

Không phải trải nghiệm cá nhân, nhưng bạn có thể nghe một tập của DNR (tôi nghĩ đó là tập này ) khi họ nói chuyện với dân gian của Microsoft về F #. Họ đã viết hầu hết hệ thống tính điểm Xbox Live, khác xa với tầm thường, sử dụng F #. Hệ thống được mở rộng quy mô trên hàng trăm máy và họ rất hài lòng với nó.


5

Tôi không biết nếu nó được sản xuất, nhưng AI cho "Con đường của đi" đã được viết bằng F #:

http://research.microsoft.com/en-us/events/techvista2010/demolist.aspx#ThePathofGo

The Path of Go: Trò chơi nghiên cứu của Microsoft dành cho Xbox 360

Bản demo này giới thiệu một trò chơi Xbox 360, dựa trên trò chơi Go, được sản xuất nội bộ tại Microsoft Research Cambridge. Go là một trong những trò chơi cờ nổi tiếng nhất ở Đông Á, nó bắt nguồn từ Trung Quốc 4000 năm trước. Đằng sau sự đơn giản lừa đảo của trò chơi ẩn chứa sự phức tạp lớn. Chỉ mất vài phút để học, nhưng phải mất cả đời để thành thạo. Mặc dù máy tính đã vượt qua các kỹ năng của con người tại Chess, nhưng việc thực hiện AI cạnh tranh cho Go vẫn là một thách thức nghiên cứu. Trò chơi được hỗ trợ bởi ba công nghệ được phát triển tại Microsoft Research Cambridge: AI có khả năng chơi Go, ngôn ngữ F # và TrueSkill ™ để phù hợp với người chơi trực tuyến. AI được triển khai trong F # và đáp ứng thách thức chạy hiệu quả trong khung nhỏ gọn .net trên Xbox 360. Trò chơi này đưa bạn vào một số cảnh 3D trực quan tuyệt đẹp. Nó được phát triển đầy đủ trong mã được quản lý bằng môi trường XNA.

(Đã có người khác đề cập đến "TrueSkill".)


Hấp dẫn: F # chạy trên khung nhỏ gọn trên XBox. Không FSharp.Core.dll cùng với FSharp.Core.optdata FSharp.Core.sigdata tham chiếu các hội đồng không CF?
Peter McG

1
CTP vận chuyển với một FSharp riêng biệt. Đó là bản dựng cho .NETCF. (Ngoài ra còn có một FSharp. Khác cho Silverlight.)
Brian

CTP này bạn nói về cái gì?
Jwosty
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.